1use alloc::{
2 collections::{btree_map::BTreeMap, btree_set::BTreeSet},
3 string::ToString,
4 vec::Vec,
5};
6use core::f32;
7
8#[cfg(feature = "text_layout")]
9use azul_core::callbacks::{CallbackInfo, DomNodeId, InlineText};
10use azul_core::{
11 app_resources::{
12 DpiScaleFactor, Epoch, FontInstanceKey, IdNamespace, ImageCache, RendererResources,
13 ResourceUpdate, ShapedWords, WordPositions, Words,
14 },
15 callbacks::DocumentId,
16 display_list::RenderCallbacks,
17 dom::{NodeData, NodeType},
18 id_tree::{NodeDataContainer, NodeDataContainerRef, NodeDataContainerRefMut, NodeId},
19 styled_dom::{
20 ChangedCssProperty, CssPropertyCache, DomId, NodeHierarchyItem, ParentWithNodeDepth,
21 StyledDom, StyledNode, StyledNodeState,
22 },
23 traits::GetTextLayout,
24 ui_solver::{
25 GpuValueCache, HeightCalculatedRect, HorizontalSolvedPosition, LayoutResult,
26 PositionInfoInner, PositionedRectangle, RelayoutChanges, ResolvedOffsets, ScrolledNodes,
27 StyleBoxShadowOffsets, VerticalSolvedPosition, WhConstraint, WidthCalculatedRect,
28 DEFAULT_FONT_SIZE_PX,
29 },
30 window::{FullWindowState, LogicalPosition, LogicalRect, LogicalSize},
31};
32use azul_css::*;
33use rust_fontconfig::FcFontCache;
34
35const DEFAULT_FLEX_GROW_FACTOR: f32 = 0.0;
36
37#[derive(Debug)]
38pub struct WhConfig {
39 pub width: WidthConfig,
40 pub height: HeightConfig,
41}
42
43#[derive(Debug, Default)]
44pub struct WidthConfig {
45 pub exact: Option<LayoutWidth>,
46 pub max: Option<LayoutMaxWidth>,
47 pub min: Option<LayoutMinWidth>,
48 pub overflow: Option<LayoutOverflow>,
49}
50
51#[derive(Debug, Default)]
52pub struct HeightConfig {
53 pub exact: Option<LayoutHeight>,
54 pub max: Option<LayoutMaxHeight>,
55 pub min: Option<LayoutMinHeight>,
56 pub overflow: Option<LayoutOverflow>,
57}
58
59fn precalculate_wh_config(styled_dom: &StyledDom) -> NodeDataContainer<WhConfig> {
60 let css_property_cache = styled_dom.get_css_property_cache();
61 let node_data_container = styled_dom.node_data.as_container();
62
63 NodeDataContainer {
64 internal: styled_dom
65 .styled_nodes
66 .as_container()
67 .internal
68 .iter()
69 .enumerate()
70 .map(|(node_id, styled_node)| {
71 let node_id = NodeId::new(node_id);
72 WhConfig {
73 width: WidthConfig {
74 exact: css_property_cache
75 .get_width(&node_data_container[node_id], &node_id, &styled_node.state)
76 .and_then(|p| p.get_property().copied()),
77 max: css_property_cache
78 .get_max_width(
79 &node_data_container[node_id],
80 &node_id,
81 &styled_node.state,
82 )
83 .and_then(|p| p.get_property().copied()),
84 min: css_property_cache
85 .get_min_width(
86 &node_data_container[node_id],
87 &node_id,
88 &styled_node.state,
89 )
90 .and_then(|p| p.get_property().copied()),
91 overflow: css_property_cache
92 .get_overflow_x(
93 &node_data_container[node_id],
94 &node_id,
95 &styled_node.state,
96 )
97 .and_then(|p| p.get_property().copied()),
98 },
99 height: HeightConfig {
100 exact: css_property_cache
101 .get_height(&node_data_container[node_id], &node_id, &styled_node.state)
102 .and_then(|p| p.get_property().copied()),
103 max: css_property_cache
104 .get_max_height(
105 &node_data_container[node_id],
106 &node_id,
107 &styled_node.state,
108 )
109 .and_then(|p| p.get_property().copied()),
110 min: css_property_cache
111 .get_min_height(
112 &node_data_container[node_id],
113 &node_id,
114 &styled_node.state,
115 )
116 .and_then(|p| p.get_property().copied()),
117 overflow: css_property_cache
118 .get_overflow_y(
119 &node_data_container[node_id],
120 &node_id,
121 &styled_node.state,
122 )
123 .and_then(|p| p.get_property().copied()),
124 },
125 }
126 })
127 .collect(),
128 }
129}
130
131macro_rules! determine_preferred {
132 ($fn_name:ident, $width:ident) => {
133 fn $fn_name(
139 config: &WhConfig,
140 preferred_width: Option<f32>,
141 parent_width: f32,
142 parent_overflow: LayoutOverflow,
143 ) -> WhConstraint {
144 let width = config
145 .$width
146 .exact
147 .as_ref()
148 .map(|x| x.inner.to_pixels(parent_width).max(0.0));
149 let min_width = config
150 .$width
151 .min
152 .as_ref()
153 .map(|x| x.inner.to_pixels(parent_width).max(0.0));
154 let max_width = config
155 .$width
156 .max
157 .as_ref()
158 .map(|x| x.inner.to_pixels(parent_width).max(0.0));
159
160 if let Some(width) = width {
161 WhConstraint::EqualTo(
163 width
164 .min(max_width.unwrap_or(f32::MAX))
165 .max(min_width.unwrap_or(0.0)),
166 )
167 } else {
168 if let Some(max_width) = max_width {
170 WhConstraint::Between(
171 min_width
172 .unwrap_or(0.0)
173 .max(preferred_width.unwrap_or(0.0))
174 .min(max_width.min(f32::MAX)),
175 max_width.max(0.0),
176 )
177 } else {
178 if let Some(min_width) = min_width {
180 if min_width.max(preferred_width.unwrap_or(0.0)) < parent_width.max(0.0) {
181 WhConstraint::Between(
182 min_width.max(preferred_width.unwrap_or(0.0)),
183 parent_width.max(0.0),
184 )
185 } else {
186 WhConstraint::EqualTo(min_width.max(preferred_width.unwrap_or(0.0)))
187 }
188 } else {
189 if let Some(preferred_width) = preferred_width {
191 let preferred_max = preferred_width.max(0.0);
192 if preferred_max > parent_width {
193 match parent_overflow {
194 LayoutOverflow::Hidden | LayoutOverflow::Visible => {
195 WhConstraint::Between(preferred_max, core::f32::MAX)
196 }
197 LayoutOverflow::Auto | LayoutOverflow::Scroll => {
198 WhConstraint::EqualTo(parent_width)
199 }
200 }
201 } else {
202 WhConstraint::Between(preferred_max, parent_width)
203 }
204 } else {
205 match parent_overflow {
206 LayoutOverflow::Hidden | LayoutOverflow::Visible => {
207 WhConstraint::Between(0.0, core::f32::MAX)
208 }
209 LayoutOverflow::Auto | LayoutOverflow::Scroll => {
210 WhConstraint::Between(0.0, parent_width)
211 }
212 }
213 }
214 }
215 }
216 }
217 }
218 };
219}
220
221determine_preferred!(determine_preferred_width, width);
226
227determine_preferred!(determine_preferred_height, height);
232
233macro_rules! typed_arena {
248 (
249 $struct_name:ident,
250 $preferred_field:ident,
251 $determine_preferred_fn:ident,
252 $width_or_height:ident,
253 $get_padding_fn:ident,
254 $get_border_fn:ident,
255 $get_margin_fn:ident,
256 $get_flex_basis:ident,
257 $from_rect_layout_arena_fn_name:ident,
258 $bubble_fn_name:ident,
259 $apply_flex_grow_fn_name:ident,
260 $main_axis:ident,
261 $margin_left:ident,
262 $margin_right:ident,
263 $padding_left:ident,
264 $padding_right:ident,
265 $border_left:ident,
266 $border_right:ident,
267 $left:ident,
268 $right:ident,
269 ) => {
270 #[must_use]
280 fn $from_rect_layout_arena_fn_name<'a>(
281 wh_configs: &NodeDataContainerRef<'a, WhConfig>,
282 offsets: &NodeDataContainerRef<'a, AllOffsets>,
283 widths: &NodeDataContainerRef<'a, Option<f32>>,
284 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
285 node_depths: &[ParentWithNodeDepth],
286 root_size_width: f32,
287 ) -> NodeDataContainer<$struct_name> {
288 let mut new_nodes = NodeDataContainer {
290 internal: vec![$struct_name::default(); node_hierarchy.len()],
291 };
292
293 for ParentWithNodeDepth { depth: _, node_id } in node_depths.iter() {
294 let parent_id = match node_id.into_crate_internal() {
295 Some(s) => s,
296 None => continue,
297 };
298
299 let nd = &wh_configs[parent_id];
300 let parent_offsets = &offsets[parent_id];
301 let width = match widths.get(parent_id) {
302 Some(s) => *s,
303 None => continue,
304 };
305
306 let parent_parent_id = node_hierarchy
307 .get(parent_id)
308 .and_then(|t| t.parent_id())
309 .unwrap_or(NodeId::ZERO);
310
311 let parent_parent_width = new_nodes
312 .as_ref()
313 .get(parent_parent_id)
314 .map(|parent| parent.$preferred_field)
315 .unwrap_or_default()
316 .max_available_space()
317 .unwrap_or(root_size_width);
318
319 let parent_parent_overflow = wh_configs[parent_parent_id]
320 .$width_or_height
321 .overflow
322 .unwrap_or_default();
323
324 let parent_width = $determine_preferred_fn(
325 &nd,
326 width,
327 parent_parent_width,
328 parent_parent_overflow,
329 );
330
331 new_nodes.as_ref_mut()[parent_id] = $struct_name {
332 $preferred_field: parent_width,
334
335 $margin_left: parent_offsets.margin.$left.as_ref().copied(),
336 $margin_right: parent_offsets.margin.$right.as_ref().copied(),
337
338 $padding_left: parent_offsets.padding.$left.as_ref().copied(),
339 $padding_right: parent_offsets.padding.$right.as_ref().copied(),
340
341 $border_left: parent_offsets.border_widths.$left.as_ref().copied(),
342 $border_right: parent_offsets.border_widths.$right.as_ref().copied(),
343
344 $left: parent_offsets.position.$left.as_ref().copied(),
345 $right: parent_offsets.position.$right.as_ref().copied(),
346
347 box_sizing: parent_offsets.box_sizing,
348 flex_grow_px: 0.0,
349 min_inner_size_px: parent_width.min_needed_space().unwrap_or(0.0),
350 };
351
352 let parent_overflow = wh_configs[parent_id]
353 .$width_or_height
354 .overflow
355 .unwrap_or_default();
356
357 for child_id in parent_id.az_children(node_hierarchy) {
358 let nd = &wh_configs[child_id];
359 let child_offsets = &offsets[child_id];
360 let width = match widths.get(child_id) {
361 Some(s) => *s,
362 None => continue,
363 };
364 let parent_available_space = parent_width.max_available_space().unwrap_or(0.0);
365 let child_width = $determine_preferred_fn(
366 &nd,
367 width,
368 parent_available_space,
369 parent_overflow,
370 );
371 let mut child = $struct_name {
372 $preferred_field: child_width,
374
375 $margin_left: child_offsets.margin.$left.as_ref().copied(),
376 $margin_right: child_offsets.margin.$right.as_ref().copied(),
377
378 $padding_left: child_offsets.padding.$left.as_ref().copied(),
379 $padding_right: child_offsets.padding.$right.as_ref().copied(),
380
381 $border_left: child_offsets.border_widths.$left.as_ref().copied(),
382 $border_right: child_offsets.border_widths.$right.as_ref().copied(),
383
384 $left: child_offsets.position.$left.as_ref().copied(),
385 $right: child_offsets.position.$right.as_ref().copied(),
386
387 box_sizing: child_offsets.box_sizing,
388 flex_grow_px: 0.0,
389 min_inner_size_px: child_width.min_needed_space().unwrap_or(0.0),
390 };
391 let child_flex_basis = child
392 .$get_flex_basis(parent_available_space)
393 .min(child_width.max_available_space().unwrap_or(core::f32::MAX));
394 child.min_inner_size_px = child.min_inner_size_px.max(child_flex_basis);
395 new_nodes.as_ref_mut()[child_id] = child;
396 }
397 }
398
399 new_nodes
400 }
401
402 fn $bubble_fn_name<'a, 'b>(
406 node_data: &mut NodeDataContainerRefMut<'b, $struct_name>,
407 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
408 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
409 layout_directions: &NodeDataContainerRef<'a, LayoutFlexDirection>,
410 wh_configs: &NodeDataContainerRef<'a, WhConfig>,
411 node_depths: &[ParentWithNodeDepth],
412 root_size_width: f32,
413 ) {
414 for ParentWithNodeDepth { depth: _, node_id } in node_depths.iter().rev() {
419 let parent_id = match node_id.into_crate_internal() {
420 Some(s) => s,
421 None => continue,
422 };
423
424 let parent_parent_width = match node_hierarchy[parent_id].parent_id() {
425 None => root_size_width,
426 Some(s) => node_data[s]
427 .$preferred_field
428 .max_available_space()
429 .unwrap_or(root_size_width), };
431
432 let parent_width = node_data[parent_id]
433 .$preferred_field
434 .max_available_space()
435 .unwrap_or(parent_parent_width);
436 let flex_axis = layout_directions[parent_id].get_axis();
437
438 let mut children_flex_basis = 0.0_f32;
439
440 parent_id
441 .az_children(node_hierarchy)
442 .filter(|child_id| layout_positions[*child_id] != LayoutPosition::Absolute)
443 .map(|child_id| {
444 (
445 child_id,
446 node_data[child_id].min_inner_size_px
447 + node_data[child_id].$get_margin_fn(parent_width),
448 )
449 })
450 .for_each(|(_, flex_basis)| {
451 if flex_axis == LayoutAxis::$main_axis {
452 children_flex_basis += flex_basis;
453 } else {
454 children_flex_basis = children_flex_basis.max(flex_basis);
456 }
457 });
458
459 let parent_max_available_space = node_data[parent_id]
462 .$preferred_field
463 .max_available_space()
464 .unwrap_or(children_flex_basis);
465 let children_inner_width = parent_max_available_space.min(children_flex_basis);
466
467 let parent_min_inner_size_px = children_inner_width
470 + node_data[parent_id].$get_padding_fn(parent_parent_width);
471
472 node_data[parent_id].min_inner_size_px = node_data[parent_id]
474 .min_inner_size_px
475 .max(parent_min_inner_size_px);
476 }
477
478 }
481
482 fn $apply_flex_grow_fn_name<'a, 'b>(
489 node_data: &mut NodeDataContainer<$struct_name>,
490 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
491 layout_displays: &NodeDataContainerRef<'a, CssPropertyValue<LayoutDisplay>>,
492 layout_flex_grows: &NodeDataContainerRef<'a, f32>,
493 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
494 layout_directions: &NodeDataContainerRef<'a, LayoutFlexDirection>,
495 node_depths: &[ParentWithNodeDepth],
496 root_width: f32,
497 parents_to_recalc: &BTreeSet<NodeId>,
498 ) {
499 fn distribute_space_along_main_axis<'a>(
503 node_id: &NodeId,
504 children: &[NodeId],
505 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
506 layout_displays: &NodeDataContainerRef<'a, CssPropertyValue<LayoutDisplay>>,
507 layout_flex_grows: &NodeDataContainerRef<'a, f32>,
508 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
509 width_calculated_arena: &'a NodeDataContainerRef<$struct_name>,
510 root_width: f32,
511 ) -> Vec<f32> {
512 let parent_node_inner_width = {
514 let parent_node = &width_calculated_arena[*node_id];
515 let parent_parent_width = node_hierarchy[*node_id]
516 .parent_id()
517 .and_then(|p| {
518 width_calculated_arena[p]
519 .$preferred_field
520 .max_available_space()
521 })
522 .unwrap_or(root_width);
523 parent_node.total() - parent_node.$get_padding_fn(parent_parent_width)
524 };
525
526 let mut children_flex_grow = children
529 .iter()
530 .map(|child_id| {
531 if layout_positions[*child_id] != LayoutPosition::Absolute {
532 match width_calculated_arena[*child_id].$preferred_field {
534 WhConstraint::Between(min, _) => {
535 if min > width_calculated_arena[*child_id].min_inner_size_px {
536 min - width_calculated_arena[*child_id].min_inner_size_px
537 } else {
538 0.0
539 }
540 }
541 WhConstraint::EqualTo(exact) => {
542 exact - width_calculated_arena[*child_id].min_inner_size_px
543 }
544 WhConstraint::Unconstrained => 0.0,
545 }
546 } else {
547 let nearest_relative_parent_node = child_id
551 .get_nearest_matching_parent(node_hierarchy, |n| {
552 layout_positions[n].is_positioned()
553 })
554 .unwrap_or(NodeId::new(0));
555
556 let relative_parent_width = {
557 let relative_parent_node =
558 &width_calculated_arena[nearest_relative_parent_node];
559 relative_parent_node.flex_grow_px
560 + relative_parent_node.min_inner_size_px
561 };
562
563 let max_space_current_node = width_calculated_arena[*child_id]
567 .$preferred_field
568 .calculate_from_relative_parent(relative_parent_width);
569
570 if max_space_current_node
573 > width_calculated_arena[*child_id].min_inner_size_px
574 {
575 max_space_current_node
576 - width_calculated_arena[*child_id].min_inner_size_px
577 } else {
578 0.0
579 }
580 }
581 })
582 .collect::<Vec<f32>>();
583
584 let space_taken_up: f32 = children
588 .iter()
589 .enumerate()
590 .filter(|(_, child_id)| {
591 layout_positions[**child_id] != LayoutPosition::Absolute
592 })
593 .map(|(child_index_in_parent, child_id)| {
594 width_calculated_arena[*child_id].min_inner_size_px
595 + width_calculated_arena[*child_id]
596 .$get_margin_fn(parent_node_inner_width)
597 + children_flex_grow[child_index_in_parent]
598 })
599 .sum();
600
601 let mut space_available = parent_node_inner_width - space_taken_up;
604
605 if space_available <= 0.0 {
606 return children_flex_grow;
608 }
609
610 let mut variable_width_childs = children
616 .iter()
617 .enumerate()
618 .filter(|(_, id)| {
619 !width_calculated_arena[**id]
620 .$preferred_field
621 .is_fixed_constraint()
622 })
623 .filter(|(_, id)| layout_positions[**id] != LayoutPosition::Absolute)
624 .filter(|(_, id)| {
625 !(layout_displays[**id] == CssPropertyValue::Exact(LayoutDisplay::None)
626 || layout_displays[**id] == CssPropertyValue::None)
627 })
628 .filter(|(_, id)| layout_flex_grows[**id] > 0.0)
629 .map(|(index_in_parent, id)| (*id, index_in_parent))
630 .collect::<BTreeMap<NodeId, usize>>();
631
632 loop {
633 if !(space_available > 0.0) || variable_width_childs.is_empty() {
634 break;
635 }
636
637 let children_combined_flex_grow: f32 = variable_width_childs
643 .iter()
644 .map(|(child_id, _)| layout_flex_grows[*child_id])
645 .sum();
646
647 if children_combined_flex_grow <= 0.0 {
648 break;
649 }
650
651 let size_per_child = space_available / children_combined_flex_grow;
652
653 let new_iteration = variable_width_childs
655 .iter()
656 .map(|(variable_child_id, index_in_parent)| {
657 let flex_grow_of_child = layout_flex_grows[*variable_child_id];
658 let added_space_for_one_child = size_per_child * flex_grow_of_child;
659 let max_width = width_calculated_arena[*variable_child_id]
660 .$preferred_field
661 .max_available_space();
662 let current_flex_grow = children_flex_grow[*index_in_parent];
663 let current_width_of_child = {
664 width_calculated_arena[*variable_child_id].min_inner_size_px
665 + current_flex_grow
666 };
667
668 let (flex_grow_this_iteration, node_is_solved) = match max_width {
669 Some(max) => {
670 let overflow: f32 =
671 current_width_of_child + added_space_for_one_child - max;
672 if !overflow.is_sign_negative() {
673 ((max - current_width_of_child).max(0.0), true)
676 } else {
677 (added_space_for_one_child, false)
678 }
679 }
680 None => (added_space_for_one_child, false),
681 };
682
683 (
684 *variable_child_id,
685 *index_in_parent,
686 flex_grow_this_iteration,
687 node_is_solved,
688 )
689 })
690 .collect::<Vec<_>>();
691
692 for (child_id, index_in_parent, flex_grow_to_add, node_is_solved) in
693 new_iteration
694 {
695 children_flex_grow[index_in_parent] += flex_grow_to_add;
696 space_available -= flex_grow_to_add;
697 if node_is_solved {
698 variable_width_childs.remove(&child_id);
699 }
700 }
701 }
702
703 children_flex_grow
704 }
705
706 fn distribute_space_along_cross_axis<'a>(
707 parent_id: &NodeId,
708 children: &[NodeId],
709 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
710 layout_displays: &NodeDataContainerRef<'a, CssPropertyValue<LayoutDisplay>>,
711 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
712 width_calculated_arena: &'a NodeDataContainerRef<$struct_name>,
713 root_width: f32,
714 ) -> Vec<f32> {
715 let parent_node_inner_width = {
716 let parent_node = &width_calculated_arena[*parent_id];
718 let parent_parent_width = node_hierarchy[*parent_id]
719 .parent_id()
720 .and_then(|p| {
721 width_calculated_arena[p]
722 .$preferred_field
723 .max_available_space()
724 })
725 .unwrap_or(root_width);
726
727 parent_node.total() - parent_node.$get_padding_fn(parent_parent_width)
728 };
729
730 let nearest_relative_node = if layout_positions[*parent_id].is_positioned() {
731 *parent_id
732 } else {
733 parent_id
734 .get_nearest_matching_parent(node_hierarchy, |n| {
735 layout_positions[n].is_positioned()
736 })
737 .unwrap_or(NodeId::new(0))
738 };
739
740 let last_relative_node_inner_width = {
741 let last_relative_node = &width_calculated_arena[nearest_relative_node];
742 let last_relative_node_parent_width = node_hierarchy[nearest_relative_node]
743 .parent_id()
744 .and_then(|p| {
745 width_calculated_arena[p]
746 .$preferred_field
747 .max_available_space()
748 })
749 .unwrap_or(root_width);
750
751 last_relative_node.total()
752 - last_relative_node.$get_padding_fn(last_relative_node_parent_width)
753 };
754
755 children
756 .iter()
757 .map(|child_id| {
758 let parent_node_inner_width =
759 if layout_positions[*child_id] == LayoutPosition::Absolute {
760 last_relative_node_inner_width
761 } else {
762 parent_node_inner_width
763 };
764
765 let min_child_width = width_calculated_arena[*child_id].total(); let space_available = parent_node_inner_width - min_child_width;
771
772 if space_available <= 0.0
775 || layout_displays[*child_id]
776 .get_property()
777 .copied()
778 .unwrap_or_default()
779 != LayoutDisplay::Flex
780 {
781 0.0
783 } else {
784 let preferred_width = match width_calculated_arena[*child_id]
785 .$preferred_field
786 .max_available_space()
787 {
788 Some(max_width) => parent_node_inner_width.min(max_width),
789 None => parent_node_inner_width,
790 };
791 preferred_width - min_child_width
794 }
795 })
796 .collect()
797 }
798
799 use azul_css::{LayoutAxis, LayoutPosition};
800
801 let top_level_flex_basis = node_data.as_ref()[NodeId::ZERO].min_inner_size_px;
807
808 let root_preferred_width = match node_data.as_ref()[NodeId::ZERO]
811 .$preferred_field
812 .max_available_space()
813 {
814 Some(max_width) => root_width.min(max_width),
815 None => root_width,
816 };
817
818 node_data.as_ref_mut()[NodeId::ZERO].flex_grow_px =
819 root_preferred_width - top_level_flex_basis;
820
821 let mut parents_grouped_by_depth = BTreeMap::new();
822 for ParentWithNodeDepth { depth, node_id } in node_depths.iter() {
823 let parent_id = match node_id.into_crate_internal() {
824 Some(s) => s,
825 None => continue,
826 };
827 if !parents_to_recalc.contains(&parent_id) {
828 continue;
829 }
830 parents_grouped_by_depth
831 .entry(depth)
832 .or_insert_with(|| Vec::new())
833 .push(parent_id);
834 }
835
836 for (depth, parent_ids) in parents_grouped_by_depth {
837 {
839 let mut node_data_mut = node_data.as_ref_mut();
840 for parent_id in parent_ids.iter() {
841 for child_id in parent_id.az_children(node_hierarchy) {
842 node_data_mut[child_id].flex_grow_px = 0.0;
843 }
844 }
845 }
846
847 let flex_grows_in_this_depth = parent_ids
849 .iter()
850 .map(|parent_id| {
851 let children = parent_id.az_children_collect(&node_hierarchy);
852 let flex_axis = layout_directions[*parent_id].get_axis();
853
854 let result = if flex_axis == LayoutAxis::$main_axis {
855 distribute_space_along_main_axis(
856 &parent_id,
857 &children,
858 node_hierarchy,
859 layout_displays,
860 layout_flex_grows,
861 layout_positions,
862 &node_data.as_ref(),
863 root_width,
864 )
865 } else {
866 distribute_space_along_cross_axis(
867 &parent_id,
868 &children,
869 node_hierarchy,
870 layout_displays,
871 layout_positions,
872 &node_data.as_ref(),
873 root_width,
874 )
875 };
876
877 (parent_id, result)
878 })
879 .collect::<Vec<_>>();
880
881 {
883 let mut node_data_mut = node_data.as_ref_mut();
884 for (parent_id, flex_grows) in flex_grows_in_this_depth {
885 for (child_id, flex_grow_px) in parent_id
886 .az_children(node_hierarchy)
887 .zip(flex_grows.into_iter())
888 {
889 node_data_mut[child_id].flex_grow_px = flex_grow_px;
890 }
891 }
892 }
893 }
894 }
895 };
896}
897
898typed_arena!(
899 WidthCalculatedRect,
900 preferred_width,
901 determine_preferred_width,
902 width,
903 get_horizontal_padding,
904 get_horizontal_border,
905 get_horizontal_margin,
906 get_flex_basis_horizontal,
907 width_calculated_rect_arena_from_rect_layout_arena,
908 bubble_preferred_widths_to_parents,
909 width_calculated_rect_arena_apply_flex_grow,
910 Horizontal,
911 margin_left,
912 margin_right,
913 padding_left,
914 padding_right,
915 border_left,
916 border_right,
917 left,
918 right,
919);
920
921typed_arena!(
922 HeightCalculatedRect,
923 preferred_height,
924 determine_preferred_height,
925 height,
926 get_vertical_padding,
927 get_vertical_border,
928 get_vertical_margin,
929 get_flex_basis_vertical,
930 height_calculated_rect_arena_from_rect_layout_arena,
931 bubble_preferred_heights_to_parents,
932 height_calculated_rect_arena_apply_flex_grow,
933 Vertical,
934 margin_top,
935 margin_bottom,
936 padding_top,
937 padding_bottom,
938 border_top,
939 border_bottom,
940 top,
941 bottom,
942);
943
944pub(crate) fn solve_flex_layout_width<'a, 'b>(
946 width_calculated_arena: &'a mut NodeDataContainer<WidthCalculatedRect>,
947 layout_flex_grow: &NodeDataContainerRef<'a, f32>,
948 layout_displays: &NodeDataContainerRef<'a, CssPropertyValue<LayoutDisplay>>,
949 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
950 layout_directions: &NodeDataContainerRef<'a, LayoutFlexDirection>,
951 node_hierarchy: &'b NodeDataContainerRef<'a, NodeHierarchyItem>,
952 wh_configs: &NodeDataContainerRef<'a, WhConfig>,
953 node_depths: &[ParentWithNodeDepth],
954 window_width: f32,
955 parents_to_recalc: &BTreeSet<NodeId>,
956) {
957 bubble_preferred_widths_to_parents(
958 &mut width_calculated_arena.as_ref_mut(),
959 node_hierarchy,
960 layout_positions,
961 layout_directions,
962 wh_configs,
963 node_depths,
964 window_width,
965 );
966 width_calculated_rect_arena_apply_flex_grow(
967 width_calculated_arena,
968 node_hierarchy,
969 layout_displays,
970 layout_flex_grow,
971 layout_positions,
972 layout_directions,
973 node_depths,
974 window_width,
975 parents_to_recalc,
976 );
977}
978
979pub(crate) fn solve_flex_layout_height<'a, 'b>(
981 height_calculated_arena: &'a mut NodeDataContainer<HeightCalculatedRect>,
982 layout_flex_grow: &NodeDataContainerRef<'a, f32>,
983 layout_displays: &NodeDataContainerRef<'a, CssPropertyValue<LayoutDisplay>>,
984 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
985 layout_directions: &NodeDataContainerRef<'a, LayoutFlexDirection>,
986 node_hierarchy: &'b NodeDataContainerRef<'a, NodeHierarchyItem>,
987 wh_configs: &NodeDataContainerRef<'a, WhConfig>,
988 node_depths: &[ParentWithNodeDepth],
989 window_height: f32,
990 parents_to_recalc: &BTreeSet<NodeId>,
991) {
992 bubble_preferred_heights_to_parents(
993 &mut height_calculated_arena.as_ref_mut(),
994 node_hierarchy,
995 layout_positions,
996 layout_directions,
997 wh_configs,
998 node_depths,
999 window_height,
1000 );
1001 height_calculated_rect_arena_apply_flex_grow(
1002 height_calculated_arena,
1003 node_hierarchy,
1004 layout_displays,
1005 layout_flex_grow,
1006 layout_positions,
1007 layout_directions,
1008 node_depths,
1009 window_height,
1010 parents_to_recalc,
1011 );
1012}
1013
1014macro_rules! get_position {
1015 (
1016 $fn_name:ident,
1017 $width_layout:ident,
1018 $height_solved_position:ident,
1019 $solved_widths_field:ident,
1020 $left:ident,
1021 $right:ident,
1022 $margin_left:ident,
1023 $margin_right:ident,
1024 $get_padding_left:ident,
1025 $get_padding_right:ident,
1026 $axis:ident
1027 ) => {
1028 fn $fn_name<'a>(
1030 arena: &mut NodeDataContainer<$height_solved_position>,
1031 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
1032 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
1033 layout_directions: &NodeDataContainerRef<'a, LayoutFlexDirection>,
1034 layout_justify_contents: &NodeDataContainerRef<'a, LayoutJustifyContent>,
1035 node_depths: &[ParentWithNodeDepth],
1036 solved_widths: &NodeDataContainerRef<'a, $width_layout>,
1037 parents_to_solve: &BTreeSet<NodeId>,
1038 ) {
1039 fn determine_child_x_absolute<'a>(
1041 child_id: NodeId,
1042 solved_widths: &NodeDataContainerRef<'a, $width_layout>,
1043 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
1044 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
1045 ) -> f32 {
1046 let child_width_with_padding = {
1047 let child_node = &solved_widths[child_id];
1048 child_node.min_inner_size_px + child_node.flex_grow_px
1049 };
1050
1051 let child_node = &solved_widths[child_id];
1052 let child_node_parent_width = node_hierarchy[child_id]
1053 .parent_id()
1054 .map(|p| solved_widths[p].total())
1055 .unwrap_or(0.0) as f32;
1056
1057 let child_right = child_node
1058 .$right
1059 .and_then(|s| Some(s.get_property()?.inner.to_pixels(child_node_parent_width)));
1060
1061 if let Some(child_right) = child_right {
1062 let child_margin_right = child_node
1064 .$margin_right
1065 .and_then(|x| {
1066 Some(x.get_property()?.inner.to_pixels(child_node_parent_width))
1067 })
1068 .unwrap_or(0.0);
1069
1070 let last_relative_node_id = child_id
1071 .get_nearest_matching_parent(node_hierarchy, |n| {
1072 layout_positions[n].is_positioned()
1073 })
1074 .unwrap_or(NodeId::new(0));
1075
1076 let last_relative_node_outer_width =
1077 &solved_widths[last_relative_node_id].total();
1078
1079 last_relative_node_outer_width
1080 - child_width_with_padding
1081 - child_margin_right
1082 - child_right
1083 } else {
1084 let child_left = child_node.$left.and_then(|s| {
1086 Some(s.get_property()?.inner.to_pixels(child_node_parent_width))
1087 });
1088
1089 let child_margin_left = child_node
1090 .$margin_left
1091 .and_then(|x| {
1092 Some(x.get_property()?.inner.to_pixels(child_node_parent_width))
1093 })
1094 .unwrap_or(0.0);
1095
1096 child_margin_left + child_left.unwrap_or(0.0)
1097 }
1098 }
1099
1100 fn determine_child_x_along_main_axis<'a>(
1102 main_axis_alignment: LayoutJustifyContent,
1103 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
1104 solved_widths: &NodeDataContainerRef<'a, $width_layout>,
1105 child_id: NodeId,
1106 parent_x_position: f32,
1107 parent_inner_width: f32,
1108 sum_x_of_children_so_far: &f32,
1109 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
1110 ) -> (f32, f32) {
1111 use azul_css::LayoutJustifyContent::*;
1112
1113 let child_width_with_padding = solved_widths[child_id].total();
1115
1116 let child_node = &solved_widths[child_id];
1118 let child_margin_left = child_node
1119 .$margin_left
1120 .and_then(|x| Some(x.get_property()?.inner.to_pixels(parent_inner_width)))
1121 .unwrap_or(0.0);
1122 let child_margin_right = child_node
1123 .$margin_right
1124 .and_then(|x| Some(x.get_property()?.inner.to_pixels(parent_inner_width)))
1125 .unwrap_or(0.0);
1126
1127 if layout_positions[child_id] == LayoutPosition::Absolute {
1128 (
1129 determine_child_x_absolute(
1130 child_id,
1131 solved_widths,
1132 layout_positions,
1133 node_hierarchy,
1134 ),
1135 0.0,
1136 )
1137 } else {
1138 let x_of_top_left_corner = match main_axis_alignment {
1141 Start | End => {
1142 parent_x_position + *sum_x_of_children_so_far + child_margin_left
1143 }
1144 Center => {
1145 parent_x_position
1146 + ((parent_inner_width as f32 / 2.0)
1147 - ((*sum_x_of_children_so_far
1148 + child_margin_right
1149 + child_width_with_padding)
1150 as f32
1151 / 2.0))
1152 }
1153 SpaceBetween => {
1154 parent_x_position }
1156 SpaceAround => {
1157 parent_x_position }
1159 SpaceEvenly => {
1160 parent_x_position }
1162 };
1163
1164 (
1165 x_of_top_left_corner,
1166 child_margin_right + child_width_with_padding + child_margin_left,
1167 )
1168 }
1169 }
1170
1171 fn determine_child_x_along_cross_axis<'a>(
1172 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
1173 solved_widths: &NodeDataContainerRef<'a, $width_layout>,
1174 child_id: NodeId,
1175 parent_x_position: f32,
1176 parent_inner_width: f32,
1177 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
1178 ) -> f32 {
1179 let child_node = &solved_widths[child_id];
1180
1181 let child_margin_left = child_node
1182 .$margin_left
1183 .and_then(|x| Some(x.get_property()?.inner.to_pixels(parent_inner_width)))
1184 .unwrap_or(0.0);
1185
1186 if layout_positions[child_id] == LayoutPosition::Absolute {
1187 determine_child_x_absolute(
1188 child_id,
1189 solved_widths,
1190 layout_positions,
1191 node_hierarchy,
1192 )
1193 } else {
1194 parent_x_position + child_margin_left
1195 }
1196 }
1197
1198 use azul_css::{LayoutAxis, LayoutJustifyContent::*};
1199
1200 for ParentWithNodeDepth { depth: _, node_id } in node_depths.iter() {
1201 let parent_id = match node_id.into_crate_internal() {
1202 Some(s) => s,
1203 None => continue,
1204 };
1205
1206 if !parents_to_solve.contains(&parent_id) {
1207 continue;
1208 }
1209
1210 let parent_node = &solved_widths[parent_id];
1211 let parent_parent_width = node_hierarchy[parent_id]
1212 .parent_id()
1213 .map(|p| solved_widths[p].total())
1214 .unwrap_or(0.0) as f32;
1215
1216 let parent_padding_left = parent_node.$get_padding_left(parent_parent_width);
1217 let parent_padding_right = parent_node.$get_padding_right(parent_parent_width);
1218
1219 let parent_x_position = arena.as_ref()[parent_id].0 + parent_padding_left;
1220 let parent_direction = layout_directions[parent_id];
1221
1222 let parent_inner_width =
1223 { parent_node.total() - (parent_padding_left + parent_padding_right) };
1224
1225 if parent_direction.get_axis() == LayoutAxis::$axis {
1226 let main_axis_alignment = layout_justify_contents[parent_id];
1228 let mut sum_x_of_children_so_far = 0.0;
1229
1230 if parent_direction.is_reverse() {
1231 for child_id in parent_id.az_reverse_children(node_hierarchy) {
1232 let (x, x_to_add) = determine_child_x_along_main_axis(
1233 main_axis_alignment,
1234 layout_positions,
1235 solved_widths,
1236 child_id,
1237 parent_x_position,
1238 parent_inner_width,
1239 &sum_x_of_children_so_far,
1240 node_hierarchy,
1241 );
1242 arena.as_ref_mut()[child_id].0 = x;
1243 sum_x_of_children_so_far += x_to_add;
1244 }
1245 } else {
1246 for child_id in parent_id.az_children(node_hierarchy) {
1247 let (x, x_to_add) = determine_child_x_along_main_axis(
1248 main_axis_alignment,
1249 layout_positions,
1250 solved_widths,
1251 child_id,
1252 parent_x_position,
1253 parent_inner_width,
1254 &sum_x_of_children_so_far,
1255 node_hierarchy,
1256 );
1257 arena.as_ref_mut()[child_id].0 = x;
1258 sum_x_of_children_so_far += x_to_add;
1259 }
1260 }
1261
1262 let should_align_towards_end = (parent_direction.is_reverse()
1266 && main_axis_alignment == Start)
1267 || (!parent_direction.is_reverse() && main_axis_alignment == End);
1268
1269 if should_align_towards_end {
1270 let diff = parent_inner_width - sum_x_of_children_so_far;
1271 for child_id in parent_id
1272 .az_children(node_hierarchy)
1273 .filter(|ch| layout_positions[*ch] != LayoutPosition::Absolute)
1274 {
1275 arena.as_ref_mut()[child_id].0 += diff;
1276 }
1277 }
1278 } else {
1279 if parent_direction.is_reverse() {
1282 for child_id in parent_id.az_reverse_children(node_hierarchy) {
1283 arena.as_ref_mut()[child_id].0 = determine_child_x_along_cross_axis(
1284 layout_positions,
1285 solved_widths,
1286 child_id,
1287 parent_x_position,
1288 parent_inner_width,
1289 node_hierarchy,
1290 );
1291 }
1292 } else {
1293 for child_id in parent_id.az_children(node_hierarchy) {
1294 arena.as_ref_mut()[child_id].0 = determine_child_x_along_cross_axis(
1295 layout_positions,
1296 solved_widths,
1297 child_id,
1298 parent_x_position,
1299 parent_inner_width,
1300 node_hierarchy,
1301 );
1302 }
1303 }
1304 }
1305 }
1306 }
1307 };
1308}
1309
1310fn get_x_positions<'a>(
1311 arena: &mut NodeDataContainer<HorizontalSolvedPosition>,
1312 solved_widths: &NodeDataContainerRef<'a, WidthCalculatedRect>,
1313 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
1314 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
1315 layout_directions: &NodeDataContainerRef<'a, LayoutFlexDirection>,
1316 layout_justify_contents: &NodeDataContainerRef<'a, LayoutJustifyContent>,
1317 node_depths: &[ParentWithNodeDepth],
1318 origin: LogicalPosition,
1319 parents_to_solve: &BTreeSet<NodeId>,
1320) {
1321 get_position!(
1322 get_pos_x,
1323 WidthCalculatedRect,
1324 HorizontalSolvedPosition,
1325 solved_widths,
1326 left,
1327 right,
1328 margin_left,
1329 margin_right,
1330 get_padding_left,
1331 get_padding_right,
1332 Horizontal
1333 );
1334
1335 get_pos_x(
1336 arena,
1337 node_hierarchy,
1338 layout_positions,
1339 layout_directions,
1340 layout_justify_contents,
1341 node_depths,
1342 solved_widths,
1343 &parents_to_solve,
1344 );
1345
1346 for item in arena.internal.iter_mut() {
1348 item.0 += origin.x;
1349 }
1350}
1351
1352fn get_y_positions<'a>(
1353 arena: &mut NodeDataContainer<VerticalSolvedPosition>,
1354 solved_heights: &NodeDataContainerRef<'a, HeightCalculatedRect>,
1355 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
1356 layout_positions: &NodeDataContainerRef<'a, LayoutPosition>,
1357 layout_directions: &NodeDataContainerRef<'a, LayoutFlexDirection>,
1358 layout_justify_contents: &NodeDataContainerRef<'a, LayoutJustifyContent>,
1359 node_depths: &[ParentWithNodeDepth],
1360 origin: LogicalPosition,
1361 parents_to_solve: &BTreeSet<NodeId>,
1362) {
1363 get_position!(
1364 get_pos_y,
1365 HeightCalculatedRect,
1366 VerticalSolvedPosition,
1367 solved_heights,
1368 top,
1369 bottom,
1370 margin_top,
1371 margin_bottom,
1372 get_padding_top,
1373 get_padding_bottom,
1374 Vertical
1375 );
1376
1377 get_pos_y(
1378 arena,
1379 node_hierarchy,
1380 layout_positions,
1381 layout_directions,
1382 layout_justify_contents,
1383 node_depths,
1384 solved_heights,
1385 &parents_to_solve,
1386 );
1387
1388 for item in arena.internal.iter_mut() {
1390 item.0 += origin.y;
1391 }
1392}
1393
1394#[inline]
1395pub fn get_layout_positions<'a>(styled_dom: &StyledDom) -> NodeDataContainer<LayoutPosition> {
1396 let cache = styled_dom.get_css_property_cache();
1397 let node_data_container = styled_dom.node_data.as_container();
1398 let styled_nodes = styled_dom.styled_nodes.as_container();
1399 assert!(node_data_container.internal.len() == styled_nodes.internal.len()); NodeDataContainer {
1401 internal: styled_nodes
1402 .internal
1403 .iter()
1404 .enumerate()
1405 .map(|(node_id, styled_node)| {
1406 cache
1407 .get_position(
1408 &node_data_container.internal[node_id],
1409 &NodeId::new(node_id),
1410 &styled_node.state,
1411 )
1412 .cloned()
1413 .unwrap_or_default()
1414 .get_property_or_default()
1415 .unwrap_or_default()
1416 })
1417 .collect(),
1418 }
1419}
1420
1421#[inline]
1422pub fn get_layout_justify_contents<'a>(
1423 styled_dom: &StyledDom,
1424) -> NodeDataContainer<LayoutJustifyContent> {
1425 let cache = styled_dom.get_css_property_cache();
1426 let node_data_container = styled_dom.node_data.as_container();
1427 let styled_nodes = styled_dom.styled_nodes.as_container();
1428 assert!(node_data_container.internal.len() == styled_nodes.internal.len()); NodeDataContainer {
1431 internal: styled_nodes
1432 .internal
1433 .iter()
1434 .enumerate()
1435 .map(|(node_id, styled_node)| {
1436 cache
1437 .get_justify_content(
1438 &node_data_container.internal[node_id],
1439 &NodeId::new(node_id),
1440 &styled_node.state,
1441 )
1442 .cloned()
1443 .unwrap_or_default()
1444 .get_property_or_default()
1445 .unwrap_or_default()
1446 })
1447 .collect(),
1448 }
1449}
1450
1451#[inline]
1452pub fn get_layout_flex_directions<'a>(
1453 styled_dom: &StyledDom,
1454) -> NodeDataContainer<LayoutFlexDirection> {
1455 let cache = styled_dom.get_css_property_cache();
1456 let node_data_container = styled_dom.node_data.as_container();
1457 let styled_nodes = styled_dom.styled_nodes.as_container();
1458 assert!(node_data_container.internal.len() == styled_nodes.internal.len()); NodeDataContainer {
1461 internal: styled_nodes
1462 .internal
1463 .iter()
1464 .enumerate()
1465 .map(|(node_id, styled_node)| {
1466 cache
1467 .get_flex_direction(
1468 &node_data_container.internal[node_id],
1469 &NodeId::new(node_id),
1470 &styled_node.state,
1471 )
1472 .cloned()
1473 .unwrap_or_default()
1474 .get_property_or_default()
1475 .unwrap_or_default()
1476 })
1477 .collect(),
1478 }
1479}
1480
1481#[inline]
1482pub fn get_layout_flex_grows<'a>(styled_dom: &StyledDom) -> NodeDataContainer<f32> {
1483 let cache = styled_dom.get_css_property_cache();
1485 let node_data_container = styled_dom.node_data.as_container();
1486 let styled_nodes = styled_dom.styled_nodes.as_container();
1487 assert!(node_data_container.internal.len() == styled_nodes.internal.len()); NodeDataContainer {
1490 internal: styled_nodes
1491 .internal
1492 .iter()
1493 .enumerate()
1494 .map(|(node_id, styled_node)| {
1495 cache
1496 .get_flex_grow(
1497 &node_data_container.internal[node_id],
1498 &NodeId::new(node_id),
1499 &styled_node.state,
1500 )
1501 .and_then(|g| g.get_property().copied())
1502 .and_then(|grow| Some(grow.inner.get().max(0.0)))
1503 .unwrap_or(DEFAULT_FLEX_GROW_FACTOR)
1504 })
1505 .collect(),
1506 }
1507}
1508
1509#[inline]
1510pub fn get_layout_displays<'a>(
1511 styled_dom: &StyledDom,
1512) -> NodeDataContainer<CssPropertyValue<LayoutDisplay>> {
1513 let cache = styled_dom.get_css_property_cache();
1515 let node_data_container = styled_dom.node_data.as_container();
1516 let styled_nodes = styled_dom.styled_nodes.as_container();
1517 assert!(node_data_container.internal.len() == styled_nodes.internal.len()); NodeDataContainer {
1520 internal: styled_nodes
1521 .internal
1522 .iter()
1523 .enumerate()
1524 .map(|(node_id, styled_node)| {
1525 cache
1526 .get_display(
1527 &node_data_container.internal[node_id],
1528 &NodeId::new(node_id),
1529 &styled_node.state,
1530 )
1531 .copied()
1532 .unwrap_or(CssPropertyValue::Auto)
1533 })
1534 .collect(),
1535 }
1536}
1537
1538fn precalculate_all_offsets(styled_dom: &StyledDom) -> NodeDataContainer<AllOffsets> {
1539 let css_property_cache = styled_dom.get_css_property_cache();
1540 let node_data_container = styled_dom.node_data.as_container();
1541 let styled_nodes = styled_dom.styled_nodes.as_container();
1542 assert!(styled_nodes.internal.len() == node_data_container.internal.len()); NodeDataContainer {
1545 internal: styled_nodes
1546 .internal
1547 .iter()
1548 .enumerate()
1549 .map(|(node_id_usize, styled_node)| {
1550 let node_id = NodeId::new(node_id_usize);
1551 let state = &styled_node.state;
1552 precalculate_offset(
1553 &node_data_container.internal[node_id_usize],
1554 &css_property_cache,
1555 &node_id,
1556 state,
1557 )
1558 })
1559 .collect(),
1560 }
1561}
1562
1563struct AllOffsets {
1564 position: LayoutAbsolutePositions,
1565 border_widths: LayoutBorderOffsets,
1566 padding: LayoutPaddingOffsets,
1567 margin: LayoutMarginOffsets,
1568 box_shadow: StyleBoxShadowOffsets,
1569 box_sizing: LayoutBoxSizing,
1570 overflow_x: LayoutOverflow,
1571 overflow_y: LayoutOverflow,
1572}
1573
1574fn precalculate_offset(
1575 node_data: &NodeData,
1576 css_property_cache: &CssPropertyCache,
1577 node_id: &NodeId,
1578 state: &StyledNodeState,
1579) -> AllOffsets {
1580 AllOffsets {
1581 border_widths: LayoutBorderOffsets {
1582 left: css_property_cache
1583 .get_border_left_width(node_data, node_id, state)
1584 .cloned(),
1585 right: css_property_cache
1586 .get_border_right_width(node_data, node_id, state)
1587 .cloned(),
1588 top: css_property_cache
1589 .get_border_top_width(node_data, node_id, state)
1590 .cloned(),
1591 bottom: css_property_cache
1592 .get_border_bottom_width(node_data, node_id, state)
1593 .cloned(),
1594 },
1595 padding: LayoutPaddingOffsets {
1596 left: css_property_cache
1597 .get_padding_left(node_data, node_id, state)
1598 .cloned(),
1599 right: css_property_cache
1600 .get_padding_right(node_data, node_id, state)
1601 .cloned(),
1602 top: css_property_cache
1603 .get_padding_top(node_data, node_id, state)
1604 .cloned(),
1605 bottom: css_property_cache
1606 .get_padding_bottom(node_data, node_id, state)
1607 .cloned(),
1608 },
1609 margin: LayoutMarginOffsets {
1610 left: css_property_cache
1611 .get_margin_left(node_data, node_id, state)
1612 .cloned(),
1613 right: css_property_cache
1614 .get_margin_right(node_data, node_id, state)
1615 .cloned(),
1616 top: css_property_cache
1617 .get_margin_top(node_data, node_id, state)
1618 .cloned(),
1619 bottom: css_property_cache
1620 .get_margin_bottom(node_data, node_id, state)
1621 .cloned(),
1622 },
1623 box_shadow: StyleBoxShadowOffsets {
1624 left: css_property_cache
1625 .get_box_shadow_left(node_data, node_id, state)
1626 .cloned(),
1627 right: css_property_cache
1628 .get_box_shadow_right(node_data, node_id, state)
1629 .cloned(),
1630 top: css_property_cache
1631 .get_box_shadow_top(node_data, node_id, state)
1632 .cloned(),
1633 bottom: css_property_cache
1634 .get_box_shadow_bottom(node_data, node_id, state)
1635 .cloned(),
1636 },
1637 position: LayoutAbsolutePositions {
1638 left: css_property_cache
1639 .get_left(node_data, node_id, state)
1640 .cloned(),
1641 right: css_property_cache
1642 .get_right(node_data, node_id, state)
1643 .cloned(),
1644 top: css_property_cache
1645 .get_top(node_data, node_id, state)
1646 .cloned(),
1647 bottom: css_property_cache
1648 .get_bottom(node_data, node_id, state)
1649 .cloned(),
1650 },
1651 box_sizing: css_property_cache
1652 .get_box_sizing(node_data, node_id, state)
1653 .cloned()
1654 .unwrap_or_default()
1655 .get_property()
1656 .copied()
1657 .unwrap_or_default(),
1658 overflow_x: css_property_cache
1659 .get_overflow_x(node_data, node_id, state)
1660 .cloned()
1661 .unwrap_or_default()
1662 .get_property()
1663 .copied()
1664 .unwrap_or_default(),
1665 overflow_y: css_property_cache
1666 .get_overflow_y(node_data, node_id, state)
1667 .cloned()
1668 .unwrap_or_default()
1669 .get_property()
1670 .copied()
1671 .unwrap_or_default(),
1672 }
1673}
1674
1675struct LayoutAbsolutePositions {
1676 left: Option<CssPropertyValue<LayoutLeft>>,
1677 right: Option<CssPropertyValue<LayoutRight>>,
1678 top: Option<CssPropertyValue<LayoutTop>>,
1679 bottom: Option<CssPropertyValue<LayoutBottom>>,
1680}
1681
1682struct LayoutBorderOffsets {
1683 left: Option<CssPropertyValue<LayoutBorderLeftWidth>>,
1684 right: Option<CssPropertyValue<LayoutBorderRightWidth>>,
1685 top: Option<CssPropertyValue<LayoutBorderTopWidth>>,
1686 bottom: Option<CssPropertyValue<LayoutBorderBottomWidth>>,
1687}
1688
1689impl LayoutBorderOffsets {
1690 fn resolve(&self, parent_scale_x: f32, parent_scale_y: f32) -> ResolvedOffsets {
1691 ResolvedOffsets {
1692 left: self
1693 .left
1694 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_x)))
1695 .unwrap_or_default(),
1696 top: self
1697 .top
1698 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_y)))
1699 .unwrap_or_default(),
1700 bottom: self
1701 .bottom
1702 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_y)))
1703 .unwrap_or_default(),
1704 right: self
1705 .right
1706 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_x)))
1707 .unwrap_or_default(),
1708 }
1709 }
1710}
1711
1712struct LayoutPaddingOffsets {
1713 left: Option<CssPropertyValue<LayoutPaddingLeft>>,
1714 right: Option<CssPropertyValue<LayoutPaddingRight>>,
1715 top: Option<CssPropertyValue<LayoutPaddingTop>>,
1716 bottom: Option<CssPropertyValue<LayoutPaddingBottom>>,
1717}
1718
1719impl LayoutPaddingOffsets {
1720 fn resolve(&self, parent_scale_x: f32, parent_scale_y: f32) -> ResolvedOffsets {
1721 ResolvedOffsets {
1722 left: self
1723 .left
1724 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_x)))
1725 .unwrap_or_default(),
1726 top: self
1727 .top
1728 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_y)))
1729 .unwrap_or_default(),
1730 bottom: self
1731 .bottom
1732 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_y)))
1733 .unwrap_or_default(),
1734 right: self
1735 .right
1736 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_x)))
1737 .unwrap_or_default(),
1738 }
1739 }
1740}
1741
1742struct LayoutMarginOffsets {
1743 left: Option<CssPropertyValue<LayoutMarginLeft>>,
1744 right: Option<CssPropertyValue<LayoutMarginRight>>,
1745 top: Option<CssPropertyValue<LayoutMarginTop>>,
1746 bottom: Option<CssPropertyValue<LayoutMarginBottom>>,
1747}
1748
1749impl LayoutMarginOffsets {
1750 fn resolve(&self, parent_scale_x: f32, parent_scale_y: f32) -> ResolvedOffsets {
1751 ResolvedOffsets {
1752 left: self
1753 .left
1754 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_x)))
1755 .unwrap_or_default(),
1756 top: self
1757 .top
1758 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_y)))
1759 .unwrap_or_default(),
1760 bottom: self
1761 .bottom
1762 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_y)))
1763 .unwrap_or_default(),
1764 right: self
1765 .right
1766 .and_then(|p| Some(p.get_property()?.inner.to_pixels(parent_scale_x)))
1767 .unwrap_or_default(),
1768 }
1769 }
1770}
1771
1772struct NewIframeScrollState {
1773 dom_id: DomId,
1774 node_id: NodeId,
1775 child_rect: LogicalRect,
1776 virtual_child_rect: LogicalRect,
1777}
1778
1779#[cfg(feature = "text_layout")]
1781pub fn do_the_layout(
1782 styled_dom: StyledDom,
1783 image_cache: &ImageCache,
1784 fc_cache: &FcFontCache,
1785 renderer_resources: &mut RendererResources,
1786 current_window_dpi: DpiScaleFactor,
1787 all_resource_updates: &mut Vec<ResourceUpdate>,
1788 id_namespace: IdNamespace,
1789 document_id: &DocumentId,
1790 epoch: Epoch,
1791 callbacks: &RenderCallbacks,
1792 full_window_state: &FullWindowState,
1793) -> Vec<LayoutResult> {
1794 use azul_core::{
1795 callbacks::{HidpiAdjustedBounds, IFrameCallbackInfo, IFrameCallbackReturn},
1796 styled_dom::NodeHierarchyItemId,
1797 ui_solver::OverflowingScrollNode,
1798 };
1799
1800 let window_theme = full_window_state.theme;
1801 let mut current_dom_id = 0;
1802 let mut doms = vec![(
1803 None,
1804 DomId {
1805 inner: current_dom_id,
1806 },
1807 styled_dom,
1808 LogicalRect::new(LogicalPosition::zero(), full_window_state.size.dimensions),
1809 )];
1810 let mut resolved_doms = Vec::new();
1811 let mut new_scroll_states = Vec::new();
1812
1813 loop {
1814 let mut new_doms = Vec::new();
1815
1816 for (parent_dom_id, dom_id, styled_dom, rect) in doms.drain(..) {
1817 use azul_core::app_resources::add_fonts_and_images;
1818
1819 add_fonts_and_images(
1820 image_cache,
1821 renderer_resources,
1822 current_window_dpi,
1823 fc_cache,
1824 id_namespace,
1825 epoch,
1826 document_id,
1827 all_resource_updates,
1828 &styled_dom,
1829 callbacks.load_font_fn,
1830 callbacks.parse_font_fn,
1831 callbacks.insert_into_active_gl_textures_fn,
1832 );
1833
1834 let mut layout_result = do_the_layout_internal(
1835 dom_id,
1836 parent_dom_id,
1837 styled_dom,
1838 renderer_resources,
1839 document_id,
1840 rect,
1841 );
1842
1843 let mut iframe_mapping = BTreeMap::new();
1844
1845 for iframe_node_id in layout_result.styled_dom.scan_for_iframe_callbacks() {
1846 current_dom_id += 1;
1848 let iframe_dom_id = DomId {
1849 inner: current_dom_id,
1850 };
1851 iframe_mapping.insert(iframe_node_id, iframe_dom_id);
1852
1853 let bounds = &layout_result.rects.as_ref()[iframe_node_id];
1854 let bounds_size = LayoutSize::new(
1855 bounds.size.width.round() as isize,
1856 bounds.size.height.round() as isize,
1857 );
1858 let hidpi_bounds = HidpiAdjustedBounds::from_bounds(
1859 bounds_size,
1860 full_window_state.size.get_hidpi_factor(),
1861 );
1862
1863 let iframe_return: IFrameCallbackReturn = {
1865 let mut iframe_callback_info = IFrameCallbackInfo::new(
1866 fc_cache,
1867 image_cache,
1868 window_theme,
1869 hidpi_bounds,
1870 bounds.size,
1873 LogicalPosition::zero(),
1874 bounds.size,
1875 LogicalPosition::zero(),
1876 );
1877
1878 let mut node_data_mut = layout_result.styled_dom.node_data.as_container_mut();
1879 match &mut node_data_mut[iframe_node_id].get_iframe_node() {
1880 Some(iframe_node) => (iframe_node.callback.cb)(
1881 &mut iframe_node.data,
1882 &mut iframe_callback_info,
1883 ),
1884 None => IFrameCallbackReturn::default(),
1885 }
1886 };
1887
1888 let IFrameCallbackReturn {
1889 dom,
1890 scroll_size,
1891 scroll_offset,
1892 virtual_scroll_size,
1893 virtual_scroll_offset,
1894 } = iframe_return;
1895
1896 let mut iframe_dom = dom;
1897 let (scroll_node_id, scroll_dom_id) = match parent_dom_id {
1898 Some(s) => (iframe_node_id, s),
1899 None => (NodeId::ZERO, DomId { inner: 0 }),
1900 };
1901
1902 let hovered_nodes = full_window_state
1908 .last_hit_test
1909 .hovered_nodes
1910 .get(&iframe_dom_id)
1911 .map(|i| i.regular_hit_test_nodes.clone())
1912 .unwrap_or_default()
1913 .keys()
1914 .cloned()
1915 .collect::<Vec<_>>();
1916
1917 let active_nodes = if !full_window_state.mouse_state.mouse_down() {
1918 Vec::new()
1919 } else {
1920 hovered_nodes.clone()
1921 };
1922
1923 let _ = iframe_dom.restyle_nodes_hover(hovered_nodes.as_slice(), true);
1924 let _ = iframe_dom.restyle_nodes_active(active_nodes.as_slice(), true);
1925 if let Some(focused_node) = full_window_state.focused_node {
1926 if focused_node.dom == iframe_dom_id {
1927 let _ = iframe_dom.restyle_nodes_focus(
1928 &[focused_node.node.into_crate_internal().unwrap()],
1929 true,
1930 );
1931 }
1932 }
1933
1934 let bounds =
1936 LogicalRect::new(LogicalPosition::zero(), hidpi_bounds.get_logical_size());
1937
1938 new_doms.push((Some(dom_id), iframe_dom_id, iframe_dom, bounds));
1940 new_scroll_states.push(NewIframeScrollState {
1941 dom_id: scroll_dom_id,
1942 node_id: scroll_node_id,
1943 child_rect: LogicalRect {
1944 origin: scroll_offset,
1945 size: scroll_size,
1946 },
1947 virtual_child_rect: LogicalRect {
1948 origin: virtual_scroll_offset,
1949 size: virtual_scroll_size,
1950 },
1951 });
1952 }
1953
1954 layout_result.iframe_mapping = iframe_mapping;
1955 resolved_doms.push(layout_result);
1956 }
1957
1958 if new_doms.is_empty() {
1959 break;
1960 } else {
1961 doms = new_doms;
1962 }
1963 }
1964
1965 for nss in new_scroll_states {
1966 if let Some(lr) = resolved_doms.get_mut(nss.dom_id.inner) {
1967 let mut osn = lr
1968 .scrollable_nodes
1969 .overflowing_nodes
1970 .entry(NodeHierarchyItemId::from_crate_internal(Some(nss.node_id)))
1971 .or_insert_with(|| OverflowingScrollNode::default());
1972
1973 osn.child_rect = nss.child_rect;
1974 osn.virtual_child_rect = nss.virtual_child_rect;
1975 }
1976 }
1977
1978 resolved_doms
1979}
1980
1981#[cfg(feature = "text_layout")]
1984pub fn do_the_layout_internal(
1985 dom_id: DomId,
1986 parent_dom_id: Option<DomId>,
1987 mut styled_dom: StyledDom,
1988 renderer_resources: &mut RendererResources,
1989 document_id: &DocumentId,
1990 bounds: LogicalRect,
1991) -> LayoutResult {
1992 use azul_core::app_resources::DecodedImage;
1993
1994 let rect_size = bounds.size;
1995 let rect_offset = bounds.origin;
1996
1997 let all_parents_btreeset = styled_dom
2002 .non_leaf_nodes
2003 .iter()
2004 .filter_map(|p| Some(p.node_id.into_crate_internal()?))
2005 .collect::<BTreeSet<_>>();
2006
2007 let layout_position_info = get_layout_positions(&styled_dom);
2008 let layout_flex_grow_info = get_layout_flex_grows(&styled_dom);
2009 let layout_display_info = get_layout_displays(&styled_dom);
2010 let layout_directions_info = get_layout_flex_directions(&styled_dom);
2011 let layout_justify_contents = get_layout_justify_contents(&styled_dom);
2012 let layout_offsets = precalculate_all_offsets(&styled_dom);
2013 let layout_width_heights = precalculate_wh_config(&styled_dom);
2014
2015 let word_cache = create_word_cache(&styled_dom.node_data.as_container());
2017 let shaped_words = create_shaped_words(renderer_resources, &word_cache, &styled_dom);
2019
2020 let all_nodes_btreeset = (0..styled_dom.node_data.as_container().len())
2021 .map(|n| NodeId::new(n))
2022 .collect::<BTreeSet<_>>();
2023
2024 let display_none_nodes = get_display_none_nodes(
2026 &styled_dom.node_hierarchy.as_container(),
2027 &layout_display_info.as_ref(),
2028 );
2029 let all_word_nodes_btreeset = (0..styled_dom.node_data.as_container().len())
2030 .filter(|n| !display_none_nodes[*n]) .map(|n| NodeId::new(n)).collect::<BTreeSet<_>>();
2032
2033 let mut word_positions_no_max_width = BTreeMap::new();
2036 create_word_positions(
2037 &mut word_positions_no_max_width,
2038 &all_word_nodes_btreeset,
2039 renderer_resources,
2040 &word_cache,
2041 &shaped_words,
2042 &styled_dom,
2043 None,
2044 );
2045
2046 let mut content_widths_pre = styled_dom
2049 .node_data
2050 .as_container_mut()
2051 .transform_multithread(|node_data, node_id| {
2052 if display_none_nodes[node_id.index()] {
2053 None
2054 } else {
2055 match node_data.get_node_type() {
2056 NodeType::Image(i) => match i.get_data() {
2057 DecodedImage::NullImage { width, .. } => Some(*width as f32),
2058 DecodedImage::Gl(tex) => Some(tex.size.width as f32),
2059 DecodedImage::Raw((desc, _)) => Some(desc.width as f32),
2060 _ => None,
2061 },
2062 _ => None,
2063 }
2064 }
2065 });
2066
2067 for (node_id, word_positions) in word_positions_no_max_width.iter() {
2068 content_widths_pre.as_ref_mut()[*node_id] = Some(word_positions.0.content_size.width);
2069 }
2070
2071 let mut width_calculated_arena = width_calculated_rect_arena_from_rect_layout_arena(
2072 &layout_width_heights.as_ref(),
2073 &layout_offsets.as_ref(),
2074 &content_widths_pre.as_ref(),
2075 &styled_dom.node_hierarchy.as_container(),
2076 &styled_dom.non_leaf_nodes.as_ref(),
2077 rect_size.width,
2078 );
2079
2080 display_none_nodes
2081 .iter()
2082 .zip(width_calculated_arena.as_ref_mut().internal.iter_mut())
2083 .for_each(|(display_none, width)| {
2084 if *display_none {
2085 *width = WidthCalculatedRect::default();
2086 }
2087 });
2088
2089 solve_flex_layout_width(
2090 &mut width_calculated_arena,
2091 &layout_flex_grow_info.as_ref(),
2092 &layout_display_info.as_ref(),
2093 &layout_position_info.as_ref(),
2094 &layout_directions_info.as_ref(),
2095 &styled_dom.node_hierarchy.as_container(),
2096 &layout_width_heights.as_ref(),
2097 styled_dom.non_leaf_nodes.as_ref(),
2098 rect_size.width,
2099 &all_parents_btreeset,
2100 );
2101
2102 let word_blocks_to_recalculate = word_positions_no_max_width
2105 .iter()
2106 .filter_map(|(node_id, word_positions)| {
2107 let parent_id = styled_dom.node_hierarchy.as_container()[*node_id]
2108 .parent_id()
2109 .unwrap_or(NodeId::ZERO);
2110 if width_calculated_arena.as_ref()[*node_id].total()
2111 < word_positions.0.content_size.width
2112 {
2113 Some(*node_id)
2114 } else if width_calculated_arena.as_ref()[*node_id].total()
2116 > width_calculated_arena.as_ref()[parent_id].total()
2117 {
2118 Some(*node_id)
2119 } else {
2120 None
2121 }
2122 })
2123 .collect::<BTreeSet<_>>();
2124
2125 create_word_positions(
2127 &mut word_positions_no_max_width,
2128 &word_blocks_to_recalculate,
2129 renderer_resources,
2130 &word_cache,
2131 &shaped_words,
2132 &styled_dom,
2133 Some(&width_calculated_arena.as_ref()),
2134 );
2135 let word_positions_with_max_width = word_positions_no_max_width;
2136
2137 let mut content_heights_pre = styled_dom
2139 .node_data
2140 .as_container_mut()
2141 .transform_multithread(|node_data, node_id| {
2142 let (raw_width, raw_height) = if display_none_nodes[node_id.index()] {
2143 None
2144 } else {
2145 match node_data.get_node_type() {
2146 NodeType::Image(i) => match i.get_data() {
2147 DecodedImage::NullImage { width, height, .. } => {
2148 Some((*width as f32, *height as f32))
2149 }
2150 DecodedImage::Gl(tex) => {
2151 Some((tex.size.width as f32, tex.size.height as f32))
2152 }
2153 DecodedImage::Raw((desc, _)) => {
2154 Some((desc.width as f32, desc.height as f32))
2155 }
2156 _ => None,
2157 },
2158 _ => None,
2159 }
2160 }?;
2161
2162 let current_width = width_calculated_arena.as_ref()[node_id].total();
2163
2164 Some(raw_height / raw_width * current_width)
2166 });
2167 for (node_id, word_positions) in word_positions_with_max_width.iter() {
2168 content_heights_pre.as_ref_mut()[*node_id] = Some(word_positions.0.content_size.height);
2169 }
2170
2171 let mut height_calculated_arena = height_calculated_rect_arena_from_rect_layout_arena(
2173 &layout_width_heights.as_ref(),
2174 &layout_offsets.as_ref(),
2175 &content_heights_pre.as_ref(),
2176 &styled_dom.node_hierarchy.as_container(),
2177 &styled_dom.non_leaf_nodes.as_ref(),
2178 rect_size.height,
2179 );
2180
2181 display_none_nodes
2182 .iter()
2183 .zip(height_calculated_arena.as_ref_mut().internal.iter_mut())
2184 .for_each(|(display_none, height)| {
2185 if *display_none {
2186 *height = HeightCalculatedRect::default();
2187 }
2188 });
2189
2190 solve_flex_layout_height(
2191 &mut height_calculated_arena,
2192 &layout_flex_grow_info.as_ref(),
2193 &layout_display_info.as_ref(),
2194 &layout_position_info.as_ref(),
2195 &layout_directions_info.as_ref(),
2196 &styled_dom.node_hierarchy.as_container(),
2197 &layout_width_heights.as_ref(),
2198 styled_dom.non_leaf_nodes.as_ref(),
2199 rect_size.height,
2200 &all_parents_btreeset,
2201 );
2202
2203 let mut x_positions = NodeDataContainer {
2204 internal: vec![HorizontalSolvedPosition(0.0); styled_dom.node_data.len()].into(),
2205 };
2206
2207 get_x_positions(
2208 &mut x_positions,
2209 &width_calculated_arena.as_ref(),
2210 &styled_dom.node_hierarchy.as_container(),
2211 &layout_position_info.as_ref(),
2212 &layout_directions_info.as_ref(),
2213 &layout_justify_contents.as_ref(),
2214 &styled_dom.non_leaf_nodes.as_ref(),
2215 rect_offset.clone(),
2216 &all_parents_btreeset,
2217 );
2218
2219 let mut y_positions = NodeDataContainer {
2220 internal: vec![VerticalSolvedPosition(0.0); styled_dom.node_data.as_ref().len()].into(),
2221 };
2222
2223 get_y_positions(
2224 &mut y_positions,
2225 &height_calculated_arena.as_ref(),
2226 &styled_dom.node_hierarchy.as_container(),
2227 &layout_position_info.as_ref(),
2228 &layout_directions_info.as_ref(),
2229 &layout_justify_contents.as_ref(),
2230 &styled_dom.non_leaf_nodes.as_ref(),
2231 rect_offset,
2232 &all_parents_btreeset,
2233 );
2234
2235 let mut positioned_rects = NodeDataContainer {
2236 internal: vec![PositionedRectangle::default(); styled_dom.node_data.len()].into(),
2237 };
2238 let nodes_that_updated_positions = all_nodes_btreeset.clone();
2239 let nodes_that_need_to_redraw_text = all_nodes_btreeset.clone();
2240
2241 position_nodes(
2242 &mut positioned_rects.as_ref_mut(),
2243 &styled_dom,
2244 AllOffsetsProvider::All(&layout_offsets.as_ref()),
2245 &width_calculated_arena.as_ref(),
2246 &height_calculated_arena.as_ref(),
2247 &x_positions.as_ref(),
2248 &y_positions.as_ref(),
2249 &nodes_that_updated_positions,
2250 &nodes_that_need_to_redraw_text,
2251 &layout_position_info.as_ref(),
2252 &word_cache,
2253 &shaped_words,
2254 &word_positions_with_max_width,
2255 document_id,
2256 );
2257
2258 let mut overflowing_rects = ScrolledNodes::default();
2259 get_nodes_that_need_scroll_clip(
2260 &mut overflowing_rects,
2261 &styled_dom.styled_nodes.as_container(),
2262 &styled_dom.node_data.as_container(),
2263 &styled_dom.node_hierarchy.as_container(),
2264 &positioned_rects.as_ref(),
2265 styled_dom.non_leaf_nodes.as_ref(),
2266 dom_id,
2267 document_id,
2268 );
2269
2270 let mut gpu_value_cache = GpuValueCache::empty();
2271 let _ = gpu_value_cache.synchronize(&positioned_rects.as_ref(), &styled_dom);
2272
2273 LayoutResult {
2274 dom_id,
2275 parent_dom_id,
2276 styled_dom,
2277 root_size: LayoutSize::new(
2278 rect_size.width.round() as isize,
2279 rect_size.height.round() as isize,
2280 ),
2281 root_position: LayoutPoint::new(
2282 rect_offset.x.round() as isize,
2283 rect_offset.y.round() as isize,
2284 ),
2285 preferred_widths: content_widths_pre,
2286 preferred_heights: content_heights_pre,
2287 width_calculated_rects: width_calculated_arena,
2288 height_calculated_rects: height_calculated_arena,
2289 solved_pos_x: x_positions,
2290 solved_pos_y: y_positions,
2291 layout_displays: layout_display_info,
2292 layout_flex_grows: layout_flex_grow_info,
2293 layout_positions: layout_position_info,
2294 layout_flex_directions: layout_directions_info,
2295 layout_justify_contents,
2296 rects: positioned_rects,
2297 words_cache: word_cache,
2298 shaped_words_cache: shaped_words,
2299 positioned_words_cache: word_positions_with_max_width,
2300 scrollable_nodes: overflowing_rects,
2301 iframe_mapping: BTreeMap::new(),
2302 gpu_value_cache,
2303 }
2304}
2305
2306fn get_display_none_nodes<'a, 'b>(
2308 node_hierarchy: &'b NodeDataContainerRef<'a, NodeHierarchyItem>,
2309 layout_displays: &NodeDataContainerRef<'a, CssPropertyValue<LayoutDisplay>>,
2310) -> Vec<bool> {
2311 let mut items_that_should_be_set_to_zero = vec![false; layout_displays.internal.len()];
2312 if layout_displays.internal.is_empty() || layout_displays.internal.len() != node_hierarchy.len()
2313 {
2314 return items_that_should_be_set_to_zero;
2315 }
2316
2317 let mut current = 0;
2318 while current < items_that_should_be_set_to_zero.len() {
2319 let display = layout_displays.internal[current].clone();
2320
2321 if display == CssPropertyValue::None
2322 || display == CssPropertyValue::Exact(LayoutDisplay::None)
2323 {
2324 items_that_should_be_set_to_zero[current] = true;
2325
2326 let subtree_len = node_hierarchy.subtree_len(NodeId::new(current));
2328 for child_id in current..=(current + subtree_len) {
2329 items_that_should_be_set_to_zero[child_id] = true;
2330 }
2331
2332 current += subtree_len + 1;
2333 } else {
2334 current += 1;
2335 }
2336 }
2337
2338 items_that_should_be_set_to_zero
2339}
2340
2341enum AllOffsetsProvider<'a> {
2351 All(&'a NodeDataContainerRef<'a, AllOffsets>),
2352 OnlyRecalculatedNodes(&'a BTreeMap<NodeId, AllOffsets>),
2353}
2354
2355impl<'a> AllOffsetsProvider<'a> {
2356 fn get_offsets_for_node(&self, node_id: &NodeId) -> Option<&AllOffsets> {
2357 match self {
2358 AllOffsetsProvider::All(a) => Some(&a[*node_id]),
2359 AllOffsetsProvider::OnlyRecalculatedNodes(b) => b.get(node_id),
2360 }
2361 }
2362}
2363
2364fn position_nodes<'a>(
2365 positioned_rects: &mut NodeDataContainerRefMut<'a, PositionedRectangle>,
2366 styled_dom: &StyledDom,
2367 offsets: AllOffsetsProvider<'a>,
2368 solved_widths: &NodeDataContainerRef<'a, WidthCalculatedRect>,
2369 solved_heights: &NodeDataContainerRef<'a, HeightCalculatedRect>,
2370 x_positions: &NodeDataContainerRef<'a, HorizontalSolvedPosition>,
2371 y_positions: &NodeDataContainerRef<'a, VerticalSolvedPosition>,
2372 nodes_that_updated_positions: &BTreeSet<NodeId>,
2373 nodes_that_need_to_redraw_text: &BTreeSet<NodeId>,
2374 position_info: &NodeDataContainerRef<'a, LayoutPosition>,
2375 word_cache: &BTreeMap<NodeId, Words>,
2376 shaped_words: &BTreeMap<NodeId, ShapedWords>,
2377 word_positions: &BTreeMap<NodeId, (WordPositions, FontInstanceKey)>,
2378 document_id: &DocumentId,
2379) {
2380 use azul_core::ui_solver::PositionInfo;
2381
2382 let mut positioned_node_stack = vec![NodeId::new(0)];
2383 let css_property_cache = styled_dom.get_css_property_cache();
2384 let styled_nodes = styled_dom.styled_nodes.as_container();
2385 let node_hierarchy = &styled_dom.node_hierarchy.as_container();
2386
2387 for ParentWithNodeDepth { depth: _, node_id } in styled_dom.non_leaf_nodes.as_ref().iter() {
2389 let parent_node_id = match node_id.into_crate_internal() {
2390 Some(s) => s,
2391 None => continue,
2392 };
2393 if !nodes_that_updated_positions.contains(&parent_node_id) {
2394 continue;
2395 };
2396
2397 let parent_position = position_info[parent_node_id];
2398 let width = solved_widths[parent_node_id];
2399 let height = solved_heights[parent_node_id];
2400 let x_pos = x_positions[parent_node_id].0;
2401 let y_pos = y_positions[parent_node_id].0;
2402
2403 let parent_parent_node_id = node_hierarchy[parent_node_id]
2404 .parent_id()
2405 .unwrap_or(NodeId::new(0));
2406 let parent_x_pos = x_positions[parent_parent_node_id].0;
2407 let parent_y_pos = y_positions[parent_parent_node_id].0;
2408 let parent_parent_width = solved_widths[parent_parent_node_id];
2409 let parent_parent_height = solved_heights[parent_parent_node_id];
2410
2411 let last_positioned_item_node_id = positioned_node_stack
2412 .last()
2413 .map(|l| *l)
2414 .unwrap_or(NodeId::new(0));
2415 let last_positioned_item_x_pos = x_positions[last_positioned_item_node_id].0;
2416 let last_positioned_item_y_pos = y_positions[last_positioned_item_node_id].0;
2417 let parent_position_info = match parent_position {
2418 LayoutPosition::Static => PositionInfo::Static(PositionInfoInner {
2419 x_offset: x_pos - parent_x_pos,
2421 y_offset: y_pos - parent_y_pos,
2422 static_x_offset: x_pos,
2423 static_y_offset: y_pos,
2424 }),
2425 LayoutPosition::Relative => PositionInfo::Relative(PositionInfoInner {
2426 x_offset: x_pos - parent_x_pos,
2428 y_offset: y_pos - parent_y_pos,
2429 static_x_offset: x_pos,
2430 static_y_offset: y_pos,
2431 }),
2432 LayoutPosition::Absolute => PositionInfo::Absolute(PositionInfoInner {
2433 x_offset: x_pos - last_positioned_item_x_pos,
2435 y_offset: y_pos - last_positioned_item_y_pos,
2436 static_x_offset: x_pos,
2437 static_y_offset: y_pos,
2438 }),
2439 LayoutPosition::Fixed => PositionInfo::Fixed(PositionInfoInner {
2440 x_offset: x_pos,
2442 y_offset: y_pos,
2443 static_x_offset: x_pos,
2444 static_y_offset: y_pos,
2445 }),
2446 };
2447 let parent_size = LogicalSize::new(width.total(), height.total());
2448
2449 let parent_offsets = match offsets.get_offsets_for_node(&parent_node_id) {
2450 Some(s) => s,
2451 None => continue,
2452 };
2453
2454 let parent_padding = parent_offsets
2455 .padding
2456 .resolve(parent_parent_width.total(), parent_parent_height.total());
2457 let parent_margin = parent_offsets
2458 .margin
2459 .resolve(parent_parent_width.total(), parent_parent_height.total());
2460 let parent_border_widths = parent_offsets
2461 .border_widths
2462 .resolve(parent_parent_width.total(), parent_parent_height.total());
2463
2464 if parent_position != LayoutPosition::Static {
2466 positioned_node_stack.push(parent_node_id);
2467 }
2468
2469 for child_node_id in parent_node_id.az_children(node_hierarchy) {
2470 let parent_width = width;
2472 let parent_height = height;
2473 let parent_x_pos = x_pos;
2474 let parent_y_pos = y_pos;
2475
2476 let width = solved_widths[child_node_id];
2477 let height = solved_heights[child_node_id];
2478 let x_pos = x_positions[child_node_id].0;
2479 let y_pos = y_positions[child_node_id].0;
2480 let child_position = position_info[child_node_id];
2481 let child_styled_node_state = &styled_nodes[child_node_id].state;
2482 let child_node_data = &styled_dom.node_data.as_container()[child_node_id];
2483
2484 let child_position = match child_position {
2485 LayoutPosition::Static => PositionInfo::Static(PositionInfoInner {
2486 x_offset: x_pos - parent_x_pos,
2488 y_offset: y_pos - parent_y_pos,
2489 static_x_offset: x_pos,
2490 static_y_offset: y_pos,
2491 }),
2492 LayoutPosition::Relative => PositionInfo::Relative(PositionInfoInner {
2493 x_offset: x_pos - parent_x_pos,
2495 y_offset: y_pos - parent_y_pos,
2496 static_x_offset: x_pos,
2497 static_y_offset: y_pos,
2498 }),
2499 LayoutPosition::Absolute => PositionInfo::Absolute(PositionInfoInner {
2500 x_offset: x_pos - last_positioned_item_x_pos,
2502 y_offset: y_pos - last_positioned_item_y_pos,
2503 static_x_offset: x_pos,
2504 static_y_offset: y_pos,
2505 }),
2506 LayoutPosition::Fixed => PositionInfo::Fixed(PositionInfoInner {
2507 x_offset: x_pos,
2509 y_offset: y_pos,
2510 static_x_offset: x_pos,
2511 static_y_offset: y_pos,
2512 }),
2513 };
2514
2515 let child_size_logical = LogicalSize::new(width.total(), height.total());
2516 let child_offsets = match offsets.get_offsets_for_node(&child_node_id) {
2517 Some(s) => s,
2518 None => continue,
2519 };
2520
2521 let child_padding = child_offsets
2522 .padding
2523 .resolve(parent_width.total(), parent_height.total());
2524 let child_margin = child_offsets
2525 .margin
2526 .resolve(parent_width.total(), parent_height.total());
2527 let child_border_widths = child_offsets
2528 .border_widths
2529 .resolve(parent_width.total(), parent_height.total());
2530
2531 let child_text = if let (Some(words), Some(shaped_words), Some((word_positions, _))) = (
2533 word_cache.get(&child_node_id),
2534 shaped_words.get(&child_node_id),
2535 word_positions.get(&child_node_id),
2536 ) {
2537 if nodes_that_need_to_redraw_text.contains(&child_node_id) {
2538 #[cfg(feature = "text_layout")]
2539 {
2540 use crate::text::InlineText;
2541
2542 let mut inline_text_layout = InlineText {
2543 words,
2544 shaped_words,
2545 }
2546 .get_text_layout(
2547 document_id,
2548 child_node_id,
2549 &word_positions.text_layout_options,
2550 );
2551
2552 let (horz_alignment, vert_alignment) = determine_text_alignment(
2553 css_property_cache
2554 .get_align_items(
2555 child_node_data,
2556 &child_node_id,
2557 child_styled_node_state,
2558 )
2559 .cloned()
2560 .and_then(|p| p.get_property_or_default())
2561 .unwrap_or_default(),
2562 css_property_cache
2563 .get_justify_content(
2564 child_node_data,
2565 &child_node_id,
2566 child_styled_node_state,
2567 )
2568 .cloned()
2569 .and_then(|p| p.get_property_or_default())
2570 .unwrap_or_default(),
2571 css_property_cache
2572 .get_text_align(
2573 child_node_data,
2574 &child_node_id,
2575 child_styled_node_state,
2576 )
2577 .cloned(),
2578 );
2579
2580 inline_text_layout
2581 .align_children_horizontal(&child_size_logical, horz_alignment);
2582 inline_text_layout.align_children_vertical_in_parent_bounds(
2583 &child_size_logical,
2584 vert_alignment,
2585 );
2586
2587 Some((
2588 word_positions.text_layout_options.clone(),
2589 inline_text_layout,
2590 ))
2591 }
2592 #[cfg(not(feature = "text_layout"))]
2593 {
2594 None
2595 }
2596 } else {
2597 positioned_rects[child_node_id]
2598 .resolved_text_layout_options
2599 .clone()
2600 }
2601 } else {
2602 None
2603 };
2604
2605 let positioned_rect = PositionedRectangle {
2606 size: LogicalSize::new(width.total(), height.total()),
2607 position: child_position,
2608 padding: child_padding,
2609 margin: child_margin,
2610 box_shadow: child_offsets.box_shadow,
2611 box_sizing: child_offsets.box_sizing,
2612 border_widths: child_border_widths,
2613 resolved_text_layout_options: child_text,
2614 overflow_x: child_offsets.overflow_x,
2615 overflow_y: child_offsets.overflow_y,
2616 };
2617
2618 positioned_rects[child_node_id] = positioned_rect;
2619 }
2620
2621 let parent_rect = &mut positioned_rects[parent_node_id];
2627 parent_rect.size = parent_size;
2628 parent_rect.position = parent_position_info;
2629 parent_rect.padding = parent_padding;
2630 parent_rect.margin = parent_margin;
2631 parent_rect.border_widths = parent_border_widths;
2632 parent_rect.box_shadow = parent_offsets.box_shadow;
2633 parent_rect.box_sizing = parent_offsets.box_sizing;
2634 parent_rect.overflow_x = parent_offsets.overflow_x;
2635 parent_rect.overflow_y = parent_offsets.overflow_y;
2636
2637 if parent_position != LayoutPosition::Static {
2638 positioned_node_stack.pop();
2639 }
2640 }
2641}
2642
2643#[cfg(feature = "text_layout")]
2644fn create_word_cache<'a>(
2645 node_data: &NodeDataContainerRef<'a, NodeData>,
2646) -> BTreeMap<NodeId, Words> {
2647 use crate::text::layout::split_text_into_words;
2648
2649 let word_map = node_data
2650 .internal
2651 .iter()
2652 .enumerate()
2653 .map(|(node_id, node)| {
2654 let node_id = NodeId::new(node_id);
2655 let string = match node.get_node_type() {
2656 NodeType::Text(string) => Some(string.as_str()),
2657 _ => None,
2658 }?;
2659 Some((node_id, split_text_into_words(string)))
2660 })
2661 .collect::<Vec<_>>();
2662
2663 word_map.into_iter().filter_map(|a| a).collect()
2664}
2665
2666#[cfg(feature = "text_layout")]
2670pub fn callback_info_shape_text(
2671 callbackinfo: &CallbackInfo,
2672 node_id: DomNodeId,
2673 text: AzString,
2674) -> Option<InlineText> {
2675 let font_ref = callbackinfo.get_font_ref(node_id)?;
2676 let text_layout_options = callbackinfo.get_text_layout_options(node_id)?;
2677 Some(crate::text::layout::shape_text(
2678 &font_ref,
2679 text.as_str(),
2680 &text_layout_options,
2681 ))
2682}
2683
2684#[cfg(feature = "text_layout")]
2685pub fn create_shaped_words<'a>(
2686 renderer_resources: &RendererResources,
2687 words: &BTreeMap<NodeId, Words>,
2688 styled_dom: &'a StyledDom,
2689) -> BTreeMap<NodeId, ShapedWords> {
2690 use crate::text::{layout::shape_words, shaping::ParsedFont};
2691
2692 let css_property_cache = styled_dom.get_css_property_cache();
2693 let styled_nodes = styled_dom.styled_nodes.as_container();
2694 let node_data = styled_dom.node_data.as_container();
2695
2696 words
2697 .iter()
2698 .filter_map(|(node_id, words)| {
2699 use azul_core::styled_dom::StyleFontFamiliesHash;
2700
2701 let styled_node_state = &styled_nodes[*node_id].state;
2702 let node_data = &node_data[*node_id];
2703 let css_font_families =
2704 css_property_cache.get_font_id_or_default(node_data, node_id, styled_node_state);
2705 let css_font_families_hash = StyleFontFamiliesHash::new(css_font_families.as_ref());
2706 let css_font_family = renderer_resources.get_font_family(&css_font_families_hash)?;
2707 let font_key = renderer_resources.get_font_key(&css_font_family)?;
2708 let (font_ref, _) = renderer_resources.get_registered_font(&font_key)?;
2709 let font_data = font_ref.get_data();
2710
2711 let parsed_font_downcasted = unsafe { &*(font_data.parsed as *const ParsedFont) };
2713
2714 let shaped_words = shape_words(words, parsed_font_downcasted);
2715
2716 Some((*node_id, shaped_words))
2717 })
2718 .collect()
2719}
2720
2721#[cfg(feature = "text_layout")]
2722fn create_word_positions<'a>(
2723 word_positions: &mut BTreeMap<NodeId, (WordPositions, FontInstanceKey)>,
2724 word_positions_to_generate: &BTreeSet<NodeId>,
2725 renderer_resources: &RendererResources,
2726 words: &BTreeMap<NodeId, Words>,
2727 shaped_words: &BTreeMap<NodeId, ShapedWords>,
2728 styled_dom: &'a StyledDom,
2729 solved_widths: Option<&'a NodeDataContainerRef<'a, WidthCalculatedRect>>,
2730) {
2731 use azul_core::{
2732 app_resources::font_size_to_au,
2733 ui_solver::{ResolvedTextLayoutOptions, DEFAULT_LETTER_SPACING, DEFAULT_WORD_SPACING},
2734 };
2735
2736 use crate::text::layout::position_words;
2737
2738 let css_property_cache = styled_dom.get_css_property_cache();
2739 let node_data_container = styled_dom.node_data.as_container();
2740
2741 let collected = words
2742 .iter()
2743 .filter_map(|(node_id, words)| {
2744 use azul_core::styled_dom::StyleFontFamiliesHash;
2745
2746 if !word_positions_to_generate.contains(node_id) {
2747 return None;
2748 }
2749 let node_data = &node_data_container[*node_id];
2750
2751 let styled_node_state = &styled_dom.styled_nodes.as_container()[*node_id].state;
2752 let font_size =
2753 css_property_cache.get_font_size_or_default(node_data, node_id, &styled_node_state);
2754 let font_size_au = font_size_to_au(font_size);
2755 let font_size_px = font_size.inner.to_pixels(DEFAULT_FONT_SIZE_PX as f32);
2756
2757 let css_font_families =
2758 css_property_cache.get_font_id_or_default(node_data, node_id, styled_node_state);
2759 let css_font_families_hash = StyleFontFamiliesHash::new(css_font_families.as_ref());
2760 let css_font_family = renderer_resources.get_font_family(&css_font_families_hash)?;
2761 let font_key = renderer_resources.get_font_key(&css_font_family)?;
2762 let (_, font_instances) = renderer_resources.get_registered_font(&font_key)?;
2763
2764 let font_instance_key = font_instances
2765 .iter()
2766 .find(|(k, v)| k.0 == font_size_au)
2767 .map(|(_, v)| v)?;
2768
2769 let shaped_words = shaped_words.get(&node_id)?;
2770
2771 let mut max_text_width = None;
2772 let mut cur_node = *node_id;
2773 while let Some(parent) = styled_dom.node_hierarchy.as_container()[*node_id].parent_id()
2774 {
2775 let overflow_x = css_property_cache
2776 .get_overflow_x(
2777 &node_data_container[parent],
2778 &parent,
2779 &styled_dom.styled_nodes.as_container()[parent].state,
2780 )
2781 .cloned();
2782
2783 match overflow_x {
2784 Some(CssPropertyValue::Exact(LayoutOverflow::Hidden))
2785 | Some(CssPropertyValue::Exact(LayoutOverflow::Visible)) => {
2786 max_text_width = None;
2787 break;
2788 }
2789 None
2790 | Some(CssPropertyValue::None)
2791 | Some(CssPropertyValue::Auto)
2792 | Some(CssPropertyValue::Initial)
2793 | Some(CssPropertyValue::Inherit)
2794 | Some(CssPropertyValue::Exact(LayoutOverflow::Auto))
2795 | Some(CssPropertyValue::Exact(LayoutOverflow::Scroll)) => {
2796 max_text_width = solved_widths.map(|sw| sw[parent].total() as f32);
2797 break;
2798 }
2799 }
2800
2801 cur_node = parent;
2802 }
2803
2804 let letter_spacing = css_property_cache
2805 .get_letter_spacing(node_data, node_id, &styled_node_state)
2806 .and_then(|ls| Some(ls.get_property()?.inner.to_pixels(DEFAULT_LETTER_SPACING)));
2807
2808 let word_spacing = css_property_cache
2809 .get_word_spacing(node_data, node_id, &styled_node_state)
2810 .and_then(|ws| Some(ws.get_property()?.inner.to_pixels(DEFAULT_WORD_SPACING)));
2811
2812 let line_height = css_property_cache
2813 .get_line_height(node_data, node_id, &styled_node_state)
2814 .and_then(|lh| Some(lh.get_property()?.inner.get()));
2815
2816 let tab_width = css_property_cache
2817 .get_tab_width(node_data, node_id, &styled_node_state)
2818 .and_then(|tw| Some(tw.get_property()?.inner.get()));
2819
2820 let text_layout_options = ResolvedTextLayoutOptions {
2821 max_horizontal_width: max_text_width.into(),
2822 leading: None.into(), holes: Vec::new().into(), font_size_px,
2825 word_spacing: word_spacing.into(),
2826 letter_spacing: letter_spacing.into(),
2827 line_height: line_height.into(),
2828 tab_width: tab_width.into(),
2829 };
2830
2831 let w = position_words(words, shaped_words, &text_layout_options);
2832
2833 Some((*node_id, (w, *font_instance_key)))
2834 })
2835 .collect::<Vec<_>>();
2836
2837 collected.into_iter().for_each(|(node_id, word_position)| {
2838 word_positions.insert(node_id, word_position);
2839 });
2840}
2841
2842fn determine_text_alignment(
2844 align_items: LayoutAlignItems,
2845 justify_content: LayoutJustifyContent,
2846 text_align: Option<CssPropertyValue<StyleTextAlign>>,
2847) -> (StyleTextAlign, StyleVerticalAlign) {
2848 let vert_alignment = match align_items {
2850 LayoutAlignItems::FlexStart => StyleVerticalAlign::Top,
2851 LayoutAlignItems::FlexEnd => StyleVerticalAlign::Bottom,
2852 _ => StyleVerticalAlign::Center,
2854 };
2855
2856 let mut horz_alignment = match justify_content {
2858 LayoutJustifyContent::Start => StyleTextAlign::Left,
2859 LayoutJustifyContent::End => StyleTextAlign::Right,
2860 _ => StyleTextAlign::Center,
2861 };
2862
2863 if let Some(text_align) = text_align
2864 .as_ref()
2865 .and_then(|ta| ta.get_property().copied())
2866 {
2867 horz_alignment = text_align;
2869 }
2870
2871 (horz_alignment, vert_alignment)
2872}
2873
2874fn get_nodes_that_need_scroll_clip(
2886 scrolled_nodes: &mut ScrolledNodes,
2887 display_list_rects: &NodeDataContainerRef<StyledNode>,
2888 dom_rects: &NodeDataContainerRef<NodeData>,
2889 node_hierarchy: &NodeDataContainerRef<NodeHierarchyItem>,
2890 layouted_rects: &NodeDataContainerRef<PositionedRectangle>,
2891 parents: &[ParentWithNodeDepth],
2892 dom_id: DomId,
2893 document_id: &DocumentId,
2894) {
2895 use azul_core::{
2896 dom::{ScrollTagId, TagId},
2897 styled_dom::NodeHierarchyItemId,
2898 ui_solver::{ExternalScrollId, OverflowingScrollNode},
2899 };
2900
2901 let mut overflowing_nodes = BTreeMap::new();
2902 let mut tags_to_node_ids = BTreeMap::new();
2903 let mut clip_nodes = BTreeMap::new();
2904
2905 let mut all_direct_overflows = parents
2907 .iter()
2908 .filter_map(|ParentWithNodeDepth { depth: _, node_id }| {
2909 let parent_id = node_id.into_crate_internal()?;
2910 let parent_rect = layouted_rects[parent_id].get_approximate_static_bounds();
2911
2912 if parent_rect.width() < 1 {
2914 return None;
2915 }
2916
2917 let children_sum_rect = LayoutRect::union(
2918 parent_id
2919 .az_children(node_hierarchy)
2920 .filter(|child_id| {
2921 use azul_core::ui_solver::PositionInfo;
2922 match layouted_rects[*child_id].position {
2923 PositionInfo::Absolute(_) => false,
2924 _ => true,
2925 }
2926 })
2927 .map(|child_id| layouted_rects[child_id].get_approximate_static_bounds()),
2928 )?;
2929
2930 if parent_rect.contains_rect(&children_sum_rect) {
2932 None
2933 } else {
2934 Some((parent_id, (parent_rect, children_sum_rect)))
2935 }
2936 })
2937 .collect::<BTreeMap<_, _>>();
2938
2939 let mut len = parents.len();
2942 while len != 0 {
2943 use azul_css::LayoutOverflow::*;
2944
2945 len -= 1;
2946
2947 let parent = &parents[len];
2948 let parent_id = match parent.node_id.into_crate_internal() {
2949 Some(s) => s,
2950 None => continue,
2951 };
2952
2953 let (parent_rect, children_sum_rect) = match all_direct_overflows.get(&parent_id).cloned() {
2954 Some(s) => s,
2955 None => continue,
2956 };
2957
2958 let positioned_rect = &layouted_rects[parent_id];
2959 let overflow_x = positioned_rect.overflow_x;
2960 let overflow_y = positioned_rect.overflow_y;
2961
2962 match (overflow_x, overflow_y) {
2963 (Hidden, Hidden) => {
2964 clip_nodes.insert(parent_id, positioned_rect.size);
2965 all_direct_overflows.remove(&parent_id);
2966 }
2967 (Visible, Visible) => {
2968 all_direct_overflows.remove(&parent_id);
2969 }
2970 _ => {
2971 }
2975 }
2976 }
2977
2978 for (parent_id, (parent_rect, children_sum_rect)) in all_direct_overflows {
2980 use azul_core::callbacks::PipelineId;
2981
2982 let parent_dom_hash = dom_rects[parent_id].calculate_node_data_hash();
2983 let parent_external_scroll_id = ExternalScrollId(
2984 parent_dom_hash.0,
2985 PipelineId(dom_id.inner as u32, document_id.id),
2986 );
2987
2988 let scroll_tag_id = match display_list_rects[parent_id].tag_id.as_ref() {
2989 Some(s) => ScrollTagId(s.into_crate_internal()),
2990 None => ScrollTagId(TagId::unique()),
2991 };
2992
2993 let child_rect = LogicalRect::new(
2994 LogicalPosition::new(
2995 children_sum_rect.origin.x as f32,
2996 children_sum_rect.origin.y as f32,
2997 ),
2998 LogicalSize::new(
2999 children_sum_rect.size.width as f32,
3000 children_sum_rect.size.height as f32,
3001 ),
3002 );
3003
3004 let os = OverflowingScrollNode {
3005 parent_rect: LogicalRect::new(
3006 LogicalPosition::new(parent_rect.origin.x as f32, parent_rect.origin.y as f32),
3007 LogicalSize::new(
3008 parent_rect.size.width as f32,
3009 parent_rect.size.height as f32,
3010 ),
3011 ),
3012 child_rect,
3013 virtual_child_rect: child_rect,
3014 parent_external_scroll_id,
3015 parent_dom_hash,
3016 scroll_tag_id,
3017 };
3018
3019 overflowing_nodes.insert(
3020 NodeHierarchyItemId::from_crate_internal(Some(parent_id)),
3021 os,
3022 );
3023 tags_to_node_ids.insert(
3024 scroll_tag_id,
3025 NodeHierarchyItemId::from_crate_internal(Some(parent_id)),
3026 );
3027 }
3028
3029 *scrolled_nodes = ScrolledNodes {
3030 overflowing_nodes,
3031 clip_nodes,
3032 tags_to_node_ids,
3033 };
3034}
3035
3036pub fn do_the_relayout(
3042 dom_id: DomId,
3043 root_bounds: LayoutRect,
3044 layout_result: &mut LayoutResult,
3045 _image_cache: &ImageCache,
3046 renderer_resources: &mut RendererResources,
3047 document_id: &DocumentId,
3048 nodes_to_relayout: Option<&BTreeMap<NodeId, Vec<ChangedCssProperty>>>,
3049 words_to_relayout: Option<&BTreeMap<NodeId, AzString>>,
3050) -> RelayoutChanges {
3051 let root_size = root_bounds.size;
3055 let root_size_changed = root_bounds != layout_result.get_bounds();
3056
3057 if !root_size_changed && nodes_to_relayout.is_none() && words_to_relayout.is_none() {
3058 return RelayoutChanges::empty();
3059 }
3060
3061 let mut nodes_to_relayout = nodes_to_relayout.map(|n| {
3063 n.iter()
3064 .filter_map(|(node_id, changed_properties)| {
3065 let mut properties = BTreeMap::new();
3066
3067 for prop in changed_properties.iter() {
3068 let prop_type = prop.previous_prop.get_type();
3069 if prop_type.can_trigger_relayout() {
3070 properties.insert(prop_type, prop.clone());
3071 }
3072 }
3073
3074 if properties.is_empty() {
3075 None
3076 } else {
3077 Some((*node_id, properties))
3078 }
3079 })
3080 .collect::<BTreeMap<NodeId, BTreeMap<CssPropertyType, ChangedCssProperty>>>()
3081 });
3082
3083 if !root_size_changed && nodes_to_relayout.is_none() && words_to_relayout.is_none() {
3084 let resized_nodes = Vec::new();
3085 let gpu_key_changes = layout_result
3086 .gpu_value_cache
3087 .synchronize(&layout_result.rects.as_ref(), &layout_result.styled_dom);
3088
3089 return RelayoutChanges {
3090 resized_nodes,
3091 gpu_key_changes,
3092 };
3093 }
3094
3095 let mut display_changed = false;
3108
3109 if let Some(nodes_to_relayout) = nodes_to_relayout.as_ref() {
3112 nodes_to_relayout
3113 .iter()
3114 .for_each(|(node_id, changed_props)| {
3115 if let Some(CssProperty::Display(new_display_state)) = changed_props
3116 .get(&CssPropertyType::Display)
3117 .map(|p| &p.current_prop)
3118 {
3119 display_changed = true;
3120 layout_result.layout_displays.as_ref_mut()[*node_id] =
3121 new_display_state.clone();
3122 }
3123
3124 if let Some(CssProperty::Position(new_position_state)) = changed_props
3125 .get(&CssPropertyType::Position)
3126 .map(|p| &p.current_prop)
3127 {
3128 layout_result.layout_positions.as_ref_mut()[*node_id] = new_position_state
3129 .get_property()
3130 .cloned()
3131 .unwrap_or_default();
3132 }
3133
3134 if let Some(CssProperty::FlexGrow(new_flex_grow)) = changed_props
3135 .get(&CssPropertyType::FlexGrow)
3136 .map(|p| &p.current_prop)
3137 {
3138 layout_result.layout_flex_grows.as_ref_mut()[*node_id] = new_flex_grow
3139 .get_property()
3140 .cloned()
3141 .map(|grow| grow.inner.get().max(0.0))
3142 .unwrap_or(DEFAULT_FLEX_GROW_FACTOR);
3143 }
3144
3145 if let Some(CssProperty::FlexDirection(new_flex_direction)) = changed_props
3146 .get(&CssPropertyType::FlexDirection)
3147 .map(|p| &p.current_prop)
3148 {
3149 layout_result.layout_flex_directions.as_ref_mut()[*node_id] =
3150 new_flex_direction
3151 .get_property()
3152 .cloned()
3153 .unwrap_or_default();
3154 }
3155
3156 if let Some(CssProperty::JustifyContent(new_justify_content)) = changed_props
3157 .get(&CssPropertyType::JustifyContent)
3158 .map(|p| &p.current_prop)
3159 {
3160 layout_result.layout_justify_contents.as_ref_mut()[*node_id] =
3161 new_justify_content
3162 .get_property()
3163 .cloned()
3164 .unwrap_or_default();
3165 }
3166 });
3167 }
3168
3169 let mut nodes_that_need_to_bubble_width = BTreeMap::new();
3170 let mut nodes_that_need_to_bubble_height = BTreeMap::new();
3171 let mut parents_that_need_to_recalc_width_of_children = BTreeSet::new();
3172 let mut parents_that_need_to_recalc_height_of_children = BTreeSet::new();
3173 let mut parents_that_need_to_reposition_children_x = BTreeSet::new();
3174 let mut parents_that_need_to_reposition_children_y = BTreeSet::new();
3175
3176 if root_size_changed {
3187 let all_parents_btreeset = layout_result
3188 .styled_dom
3189 .non_leaf_nodes
3190 .iter()
3191 .filter_map(|p| Some(p.node_id.into_crate_internal()?))
3192 .collect::<BTreeSet<_>>();
3193 layout_result.root_size = root_size;
3194 parents_that_need_to_recalc_width_of_children
3195 .extend(all_parents_btreeset.clone().into_iter());
3196 parents_that_need_to_recalc_height_of_children.extend(all_parents_btreeset.into_iter());
3197 }
3198
3199 if root_size.width != layout_result.root_size.width {
3200 let root_id = layout_result.styled_dom.root.into_crate_internal().unwrap();
3201 parents_that_need_to_recalc_width_of_children.insert(root_id);
3202 }
3203
3204 if root_size.height != layout_result.root_size.height {
3205 let root_id = layout_result.styled_dom.root.into_crate_internal().unwrap();
3206 parents_that_need_to_recalc_height_of_children.insert(root_id);
3207 }
3208
3209 let mut node_ids_that_changed_text_content = BTreeSet::new();
3210
3211 #[cfg(feature = "text_layout")]
3213 if let Some(words_to_relayout) = words_to_relayout {
3214 for (node_id, new_string) in words_to_relayout.iter() {
3215 use azul_core::{
3216 styled_dom::StyleFontFamiliesHash,
3217 ui_solver::{
3218 ResolvedTextLayoutOptions, DEFAULT_LETTER_SPACING, DEFAULT_WORD_SPACING,
3219 },
3220 };
3221
3222 use crate::text::{
3223 layout::{
3224 position_words, shape_words, split_text_into_words,
3225 word_positions_to_inline_text_layout,
3226 },
3227 shaping::ParsedFont,
3228 };
3229
3230 if layout_result.words_cache.get(&node_id).is_none() {
3231 continue;
3232 }
3233 if layout_result.shaped_words_cache.get(&node_id).is_none() {
3234 continue;
3235 }
3236 if layout_result.positioned_words_cache.get(&node_id).is_none() {
3237 continue;
3238 }
3239 let text_layout_options = match layout_result
3240 .rects
3241 .as_ref()
3242 .get(*node_id)
3243 .and_then(|s| s.resolved_text_layout_options.as_ref())
3244 {
3245 None => continue,
3246 Some(s) => s.0.clone(),
3247 };
3248
3249 let new_words = split_text_into_words(new_string.as_str());
3250
3251 let css_property_cache = layout_result.styled_dom.get_css_property_cache();
3252 let styled_nodes = layout_result.styled_dom.styled_nodes.as_container();
3253 let node_data = layout_result.styled_dom.node_data.as_container();
3254 let styled_node_state = &styled_nodes[*node_id].state;
3255 let node_data = &node_data[*node_id];
3256
3257 let css_font_families =
3258 css_property_cache.get_font_id_or_default(node_data, node_id, styled_node_state);
3259 let css_font_families_hash = StyleFontFamiliesHash::new(css_font_families.as_ref());
3260 let css_font_family = match renderer_resources.get_font_family(&css_font_families_hash)
3261 {
3262 Some(s) => s,
3263 None => continue,
3264 };
3265 let font_key = match renderer_resources.get_font_key(&css_font_family) {
3266 Some(s) => s,
3267 None => continue,
3268 };
3269 let (font_ref, _) = match renderer_resources.get_registered_font(&font_key) {
3270 Some(s) => s,
3271 None => continue,
3272 };
3273 let font_data = font_ref.get_data();
3274 let parsed_font_downcasted = unsafe { &*(font_data.parsed as *const ParsedFont) };
3275 let new_shaped_words = shape_words(&new_words, parsed_font_downcasted);
3276
3277 let font_size =
3278 css_property_cache.get_font_size_or_default(node_data, node_id, &styled_node_state);
3279 let font_size_px = font_size.inner.to_pixels(DEFAULT_FONT_SIZE_PX as f32);
3280
3281 let letter_spacing = css_property_cache
3282 .get_letter_spacing(node_data, node_id, &styled_node_state)
3283 .and_then(|ls| Some(ls.get_property()?.inner.to_pixels(DEFAULT_LETTER_SPACING)));
3284
3285 let word_spacing = css_property_cache
3286 .get_word_spacing(node_data, node_id, &styled_node_state)
3287 .and_then(|ws| Some(ws.get_property()?.inner.to_pixels(DEFAULT_WORD_SPACING)));
3288
3289 let line_height = css_property_cache
3290 .get_line_height(node_data, node_id, &styled_node_state)
3291 .and_then(|lh| Some(lh.get_property()?.inner.get()));
3292
3293 let tab_width = css_property_cache
3294 .get_tab_width(node_data, node_id, &styled_node_state)
3295 .and_then(|tw| Some(tw.get_property()?.inner.get()));
3296
3297 let new_word_positions =
3298 position_words(&new_words, &new_shaped_words, &text_layout_options);
3299 let new_inline_text_layout = word_positions_to_inline_text_layout(&new_word_positions);
3300
3301 let old_word_dimensions = layout_result
3302 .rects
3303 .as_ref()
3304 .get(*node_id)
3305 .and_then(|s| s.resolved_text_layout_options.as_ref())
3306 .map(|s| s.1.content_size)
3307 .unwrap_or(LogicalSize::zero());
3308
3309 let new_word_dimensions = new_word_positions.content_size;
3310
3311 layout_result.preferred_widths.as_ref_mut()[*node_id] =
3312 Some(new_word_positions.content_size.width);
3313 *layout_result.words_cache.get_mut(node_id).unwrap() = new_words;
3314 *layout_result.shaped_words_cache.get_mut(node_id).unwrap() = new_shaped_words;
3315 layout_result
3316 .positioned_words_cache
3317 .get_mut(node_id)
3318 .unwrap()
3319 .0 = new_word_positions;
3320 layout_result
3321 .rects
3322 .as_ref_mut()
3323 .get_mut(*node_id)
3324 .unwrap()
3325 .resolved_text_layout_options = Some((text_layout_options, new_inline_text_layout));
3326 node_ids_that_changed_text_content.insert(*node_id);
3327 }
3328 }
3329
3330 for ParentWithNodeDepth { depth: _, node_id } in layout_result.styled_dom.non_leaf_nodes.iter()
3332 {
3333 macro_rules! detect_changes {
3334 ($node_id:expr, $parent_id:expr) => {
3335 let node_data = &layout_result.styled_dom.node_data.as_container()[$node_id];
3336 let text_content_has_changed =
3337 node_ids_that_changed_text_content.contains(&$node_id);
3338 let default_changes = BTreeMap::new();
3339 let changes_for_this_node =
3340 match nodes_to_relayout.as_ref().and_then(|n| n.get(&$node_id)) {
3341 Some(s) => Some(s),
3342 None => {
3343 if text_content_has_changed {
3344 Some(&default_changes)
3345 } else {
3346 None
3347 }
3348 }
3349 };
3350
3351 let has_word_positions = layout_result
3352 .positioned_words_cache
3353 .get(&$node_id)
3354 .is_some();
3355 if let Some(changes_for_this_node) = changes_for_this_node.as_ref() {
3356 if !changes_for_this_node.is_empty()
3357 || has_word_positions
3358 || text_content_has_changed
3359 {
3360 let mut preferred_width_changed = None;
3361 let mut preferred_height_changed = None;
3362 let mut padding_x_changed = false;
3363 let mut padding_y_changed = false;
3364 let mut margin_x_changed = false;
3365 let mut margin_y_changed = false;
3366
3367 let solved_width_layout =
3368 &mut layout_result.width_calculated_rects.as_ref_mut()[$node_id];
3369 let solved_height_layout =
3370 &mut layout_result.height_calculated_rects.as_ref_mut()[$node_id];
3371 let css_property_cache = layout_result.styled_dom.get_css_property_cache();
3372 let parent_parent = layout_result.styled_dom.node_hierarchy.as_container()
3373 [$parent_id]
3374 .parent_id()
3375 .unwrap_or(NodeId::ZERO);
3376
3377 if changes_for_this_node.contains_key(&CssPropertyType::Width)
3379 || changes_for_this_node.contains_key(&CssPropertyType::MinWidth)
3380 || changes_for_this_node.contains_key(&CssPropertyType::MaxWidth)
3381 || has_word_positions
3382 || text_content_has_changed
3383 {
3384 let styled_node_state =
3385 &layout_result.styled_dom.styled_nodes.as_container()[$node_id]
3386 .state;
3387
3388 let wh_config = WhConfig {
3389 width: WidthConfig {
3390 exact: css_property_cache
3391 .get_width(node_data, &$node_id, styled_node_state)
3392 .and_then(|p| p.get_property().copied()),
3393 max: css_property_cache
3394 .get_max_width(node_data, &$node_id, styled_node_state)
3395 .and_then(|p| p.get_property().copied()),
3396 min: css_property_cache
3397 .get_min_width(node_data, &$node_id, styled_node_state)
3398 .and_then(|p| p.get_property().copied()),
3399 overflow: css_property_cache
3400 .get_overflow_x(node_data, &$node_id, styled_node_state)
3401 .and_then(|p| p.get_property().copied()),
3402 },
3403 height: HeightConfig::default(),
3404 };
3405 let parent_width = layout_result.preferred_widths.as_ref()[$parent_id]
3406 .clone()
3407 .unwrap_or(root_size.width as f32);
3408 let parent_parent_overflow_x = css_property_cache
3409 .get_overflow_x(
3410 &layout_result.styled_dom.node_data.as_container()
3411 [parent_parent],
3412 &parent_parent,
3413 &layout_result.styled_dom.styled_nodes.as_container()
3414 [parent_parent]
3415 .state,
3416 )
3417 .and_then(|p| p.get_property().copied())
3418 .unwrap_or_default();
3419
3420 let new_preferred_width = determine_preferred_width(
3421 &wh_config,
3422 layout_result.preferred_widths.as_ref()[$node_id],
3423 parent_width,
3424 parent_parent_overflow_x,
3425 );
3426
3427 if new_preferred_width != solved_width_layout.preferred_width {
3428 preferred_width_changed = Some((
3429 solved_width_layout.preferred_width,
3430 new_preferred_width,
3431 ));
3432 solved_width_layout.preferred_width = new_preferred_width;
3433 }
3434 }
3435
3436 if changes_for_this_node.contains_key(&CssPropertyType::MinHeight)
3438 || changes_for_this_node.contains_key(&CssPropertyType::MaxHeight)
3439 || changes_for_this_node.contains_key(&CssPropertyType::Height)
3440 || has_word_positions
3441 || text_content_has_changed
3442 {
3443 let styled_node_state =
3444 &layout_result.styled_dom.styled_nodes.as_container()[$node_id]
3445 .state;
3446 let wh_config = WhConfig {
3447 width: WidthConfig::default(),
3448 height: HeightConfig {
3449 exact: css_property_cache
3450 .get_height(node_data, &$node_id, &styled_node_state)
3451 .and_then(|p| p.get_property().copied()),
3452 max: css_property_cache
3453 .get_max_height(node_data, &$node_id, &styled_node_state)
3454 .and_then(|p| p.get_property().copied()),
3455 min: css_property_cache
3456 .get_min_height(node_data, &$node_id, &styled_node_state)
3457 .and_then(|p| p.get_property().copied()),
3458 overflow: css_property_cache
3459 .get_overflow_y(node_data, &$node_id, &styled_node_state)
3460 .and_then(|p| p.get_property().copied()),
3461 },
3462 };
3463 let parent_height = layout_result.preferred_heights.as_ref()
3464 [$parent_id]
3465 .clone()
3466 .unwrap_or(root_size.height as f32);
3467 let parent_parent_overflow_y = css_property_cache
3468 .get_overflow_x(
3469 &layout_result.styled_dom.node_data.as_container()
3470 [parent_parent],
3471 &parent_parent,
3472 &layout_result.styled_dom.styled_nodes.as_container()
3473 [parent_parent]
3474 .state,
3475 )
3476 .and_then(|p| p.get_property().copied())
3477 .unwrap_or_default();
3478
3479 let new_preferred_height = determine_preferred_height(
3480 &wh_config,
3481 layout_result.preferred_heights.as_ref()[$node_id],
3482 parent_height,
3483 parent_parent_overflow_y,
3484 );
3485
3486 if new_preferred_height != solved_height_layout.preferred_height {
3487 preferred_height_changed = Some((
3488 solved_height_layout.preferred_height,
3489 new_preferred_height,
3490 ));
3491 solved_height_layout.preferred_height = new_preferred_height;
3492 }
3493 }
3494
3495 if let Some(CssProperty::PaddingLeft(prop)) = changes_for_this_node
3497 .get(&CssPropertyType::PaddingLeft)
3498 .map(|p| &p.current_prop)
3499 {
3500 solved_width_layout.padding_left = Some(*prop);
3501 padding_x_changed = true;
3502 }
3503
3504 if let Some(CssProperty::PaddingRight(prop)) = changes_for_this_node
3505 .get(&CssPropertyType::PaddingRight)
3506 .map(|p| &p.current_prop)
3507 {
3508 solved_width_layout.padding_right = Some(*prop);
3509 padding_x_changed = true;
3510 }
3511
3512 if let Some(CssProperty::MarginLeft(prop)) = changes_for_this_node
3513 .get(&CssPropertyType::MarginLeft)
3514 .map(|p| &p.current_prop)
3515 {
3516 solved_width_layout.margin_left = Some(*prop);
3517 margin_x_changed = true;
3518 }
3519
3520 if let Some(CssProperty::MarginRight(prop)) = changes_for_this_node
3521 .get(&CssPropertyType::MarginRight)
3522 .map(|p| &p.current_prop)
3523 {
3524 solved_width_layout.margin_right = Some(*prop);
3525 margin_x_changed = true;
3526 }
3527
3528 if let Some(CssProperty::PaddingTop(prop)) = changes_for_this_node
3530 .get(&CssPropertyType::PaddingTop)
3531 .map(|p| &p.current_prop)
3532 {
3533 solved_height_layout.padding_top = Some(*prop);
3534 padding_y_changed = true;
3535 }
3536
3537 if let Some(CssProperty::PaddingBottom(prop)) = changes_for_this_node
3538 .get(&CssPropertyType::PaddingBottom)
3539 .map(|p| &p.current_prop)
3540 {
3541 solved_height_layout.padding_bottom = Some(*prop);
3542 padding_y_changed = true;
3543 }
3544
3545 if let Some(CssProperty::MarginTop(prop)) = changes_for_this_node
3546 .get(&CssPropertyType::MarginTop)
3547 .map(|p| &p.current_prop)
3548 {
3549 solved_height_layout.margin_top = Some(*prop);
3550 margin_y_changed = true;
3551 }
3552
3553 if let Some(CssProperty::MarginBottom(prop)) = changes_for_this_node
3554 .get(&CssPropertyType::MarginBottom)
3555 .map(|p| &p.current_prop)
3556 {
3557 solved_height_layout.margin_bottom = Some(*prop);
3558 margin_y_changed = true;
3559 }
3560
3561 if let Some((previous_preferred_width, current_preferred_width)) =
3562 preferred_width_changed
3563 {
3564 parents_that_need_to_recalc_width_of_children.insert($parent_id);
3569 nodes_that_need_to_bubble_width.insert(
3570 $node_id,
3571 (previous_preferred_width, current_preferred_width),
3572 );
3573 parents_that_need_to_recalc_width_of_children.insert($node_id);
3574 parents_that_need_to_reposition_children_x.insert($parent_id);
3575 }
3576
3577 if let Some((previous_preferred_height, current_preferred_height)) =
3578 preferred_height_changed
3579 {
3580 parents_that_need_to_recalc_height_of_children.insert($parent_id);
3585 nodes_that_need_to_bubble_height.insert(
3586 $node_id,
3587 (previous_preferred_height, current_preferred_height),
3588 );
3589 parents_that_need_to_recalc_height_of_children.insert($node_id);
3590 parents_that_need_to_reposition_children_y.insert($parent_id);
3591 }
3592
3593 if padding_x_changed {
3594 parents_that_need_to_recalc_width_of_children.insert($node_id);
3597 parents_that_need_to_reposition_children_x.insert($node_id);
3598 }
3599
3600 if padding_y_changed {
3601 parents_that_need_to_recalc_height_of_children.insert($node_id);
3605 parents_that_need_to_reposition_children_y.insert($node_id);
3606 }
3607
3608 if margin_x_changed {
3609 parents_that_need_to_recalc_width_of_children.insert($parent_id);
3612 parents_that_need_to_reposition_children_x.insert($parent_id);
3613 }
3614
3615 if margin_y_changed {
3616 parents_that_need_to_recalc_height_of_children.insert($parent_id);
3619 parents_that_need_to_reposition_children_y.insert($parent_id);
3620 }
3621
3622 }
3624 }
3625 };
3626 }
3627
3628 let node_id = match node_id.into_crate_internal() {
3629 Some(s) => s,
3630 None => continue,
3631 };
3632
3633 let parent_id = layout_result.styled_dom.node_hierarchy.as_container()[node_id]
3634 .parent_id()
3635 .unwrap_or(layout_result.styled_dom.root.into_crate_internal().unwrap());
3636
3637 detect_changes!(node_id, parent_id);
3638
3639 for child_id in node_id.az_children(&layout_result.styled_dom.node_hierarchy.as_container())
3640 {
3641 detect_changes!(child_id, node_id);
3642 }
3643 }
3644
3645 let mut rebubble_parent_widths = BTreeMap::new();
3648 let mut rebubble_parent_heights = BTreeMap::new();
3649
3650 for (node_id, (old_preferred_width, new_preferred_width)) in
3651 nodes_that_need_to_bubble_width.iter().rev()
3652 {
3653 if let Some(parent_id) =
3654 layout_result.styled_dom.node_hierarchy.as_container()[*node_id].parent_id()
3655 {
3656 let change = new_preferred_width.min_needed_space().unwrap_or(0.0)
3657 - old_preferred_width.min_needed_space().unwrap_or(0.0);
3658 layout_result.width_calculated_rects.as_ref_mut()[*node_id].min_inner_size_px =
3659 new_preferred_width.min_needed_space().unwrap_or(0.0);
3660 layout_result.width_calculated_rects.as_ref_mut()[parent_id].min_inner_size_px +=
3661 change;
3662 layout_result.width_calculated_rects.as_ref_mut()[parent_id].flex_grow_px = 0.0;
3663 if change != 0.0 {
3664 *rebubble_parent_widths
3665 .entry(parent_id)
3666 .or_insert_with(|| 0.0) += change;
3667 parents_that_need_to_recalc_width_of_children.insert(parent_id);
3668 }
3669 }
3670 }
3671
3672 for (node_id, (old_preferred_height, new_preferred_height)) in
3673 nodes_that_need_to_bubble_height.iter().rev()
3674 {
3675 if let Some(parent_id) =
3676 layout_result.styled_dom.node_hierarchy.as_container()[*node_id].parent_id()
3677 {
3678 let change = new_preferred_height.min_needed_space().unwrap_or(0.0)
3679 - old_preferred_height.min_needed_space().unwrap_or(0.0);
3680 layout_result.height_calculated_rects.as_ref_mut()[*node_id].min_inner_size_px =
3681 new_preferred_height.min_needed_space().unwrap_or(0.0);
3682 layout_result.height_calculated_rects.as_ref_mut()[parent_id].min_inner_size_px +=
3683 change;
3684 layout_result.height_calculated_rects.as_ref_mut()[parent_id].flex_grow_px = 0.0;
3685 if change != 0.0 {
3686 *rebubble_parent_heights
3687 .entry(parent_id)
3688 .or_insert_with(|| 0.0) += change;
3689 parents_that_need_to_recalc_height_of_children.insert(parent_id);
3690 }
3691 }
3692 }
3693
3694 let mut subtree_needs_relayout_width = BTreeSet::new();
3695 let mut subtree_needs_relayout_height = BTreeSet::new();
3696
3697 while !parents_that_need_to_recalc_width_of_children.is_empty() {
3699 let parents_to_extend = parents_that_need_to_recalc_width_of_children
3702 .iter()
3703 .map(|p| {
3704 layout_result.styled_dom.node_hierarchy.as_container()[*p]
3705 .parent_id()
3706 .unwrap_or(NodeId::ZERO)
3707 })
3708 .collect::<BTreeSet<_>>();
3709
3710 parents_that_need_to_recalc_width_of_children.extend(parents_to_extend.into_iter());
3711
3712 let previous_widths = parents_that_need_to_recalc_width_of_children
3713 .iter()
3714 .filter_map(|node_id| {
3715 layout_result
3716 .width_calculated_rects
3717 .as_ref()
3718 .get(*node_id)
3719 .map(|s| (node_id, *s))
3720 })
3721 .collect::<BTreeMap<_, _>>();
3722
3723 subtree_needs_relayout_width.extend(
3724 parents_that_need_to_recalc_width_of_children
3725 .iter()
3726 .cloned(),
3727 );
3728
3729 width_calculated_rect_arena_apply_flex_grow(
3730 &mut layout_result.width_calculated_rects,
3731 &layout_result.styled_dom.node_hierarchy.as_container(),
3732 &layout_result.layout_displays.as_ref(),
3733 &layout_result.layout_flex_grows.as_ref(),
3734 &layout_result.layout_positions.as_ref(),
3735 &layout_result.layout_flex_directions.as_ref(),
3736 &layout_result.styled_dom.non_leaf_nodes.as_ref(),
3737 root_size.width as f32,
3738 &parents_that_need_to_recalc_width_of_children,
3740 );
3741
3742 let parents_that_changed_width = parents_that_need_to_recalc_width_of_children
3744 .iter()
3745 .filter_map(|p| {
3746 let current_width = layout_result
3748 .width_calculated_rects
3749 .as_ref()
3750 .get(*p)
3751 .copied()?;
3752 let previous_width = previous_widths.get(p).copied()?;
3753 if current_width == previous_width {
3754 return None;
3755 }
3756 let parent_id =
3757 layout_result.styled_dom.node_hierarchy.as_container()[*p].parent_id()?;
3758 Some(parent_id)
3759 })
3760 .collect();
3761
3762 parents_that_need_to_recalc_width_of_children = parents_that_changed_width;
3764 }
3765
3766 parents_that_need_to_recalc_width_of_children = subtree_needs_relayout_width;
3767
3768 while !parents_that_need_to_recalc_height_of_children.is_empty() {
3769 let parents_to_extend = parents_that_need_to_recalc_height_of_children
3770 .iter()
3771 .map(|p| {
3772 layout_result.styled_dom.node_hierarchy.as_container()[*p]
3773 .parent_id()
3774 .unwrap_or(NodeId::ZERO)
3775 })
3776 .collect::<BTreeSet<_>>();
3777
3778 parents_that_need_to_recalc_height_of_children.extend(parents_to_extend.into_iter());
3779
3780 let previous_heights = parents_that_need_to_recalc_height_of_children
3781 .iter()
3782 .filter_map(|node_id| {
3783 layout_result
3784 .height_calculated_rects
3785 .as_ref()
3786 .get(*node_id)
3787 .map(|s| (node_id, *s))
3788 })
3789 .collect::<BTreeMap<_, _>>();
3790
3791 subtree_needs_relayout_height.extend(
3792 parents_that_need_to_recalc_height_of_children
3793 .iter()
3794 .cloned(),
3795 );
3796
3797 height_calculated_rect_arena_apply_flex_grow(
3798 &mut layout_result.height_calculated_rects,
3799 &layout_result.styled_dom.node_hierarchy.as_container(),
3800 &layout_result.layout_displays.as_ref(),
3801 &layout_result.layout_flex_grows.as_ref(),
3802 &layout_result.layout_positions.as_ref(),
3803 &layout_result.layout_flex_directions.as_ref(),
3804 &layout_result.styled_dom.non_leaf_nodes.as_ref(),
3805 root_size.height as f32,
3806 &parents_that_need_to_recalc_height_of_children,
3808 );
3809
3810 let mut parents_that_changed_height = parents_that_need_to_recalc_height_of_children
3812 .iter()
3813 .filter_map(|p| {
3814 let current_height = layout_result
3816 .height_calculated_rects
3817 .as_ref()
3818 .get(*p)
3819 .copied()?;
3820 let previous_height = previous_heights.get(p).copied()?;
3821 if current_height == previous_height {
3822 return None;
3823 }
3824 let parent_id =
3825 layout_result.styled_dom.node_hierarchy.as_container()[*p].parent_id()?;
3826 Some(parent_id)
3827 })
3828 .collect();
3829
3830 parents_that_need_to_recalc_height_of_children = parents_that_changed_height;
3832 }
3833
3834 parents_that_need_to_recalc_height_of_children = subtree_needs_relayout_height;
3835
3836 for n in parents_that_need_to_recalc_width_of_children.clone() {
3838 let subtree_parents = layout_result.styled_dom.get_subtree_parents(n);
3839 for s in subtree_parents {
3840 parents_that_need_to_recalc_width_of_children.insert(s);
3841 }
3842 parents_that_need_to_reposition_children_x.insert(n);
3843 }
3844
3845 for n in parents_that_need_to_recalc_height_of_children.clone() {
3846 let subtree_parents = layout_result.styled_dom.get_subtree_parents(n);
3847 for s in subtree_parents {
3848 parents_that_need_to_recalc_height_of_children.insert(s);
3849 }
3850 parents_that_need_to_reposition_children_y.insert(n);
3851 }
3852
3853 for n in parents_that_need_to_reposition_children_x.clone() {
3854 let subtree_parents = layout_result.styled_dom.get_subtree_parents(n);
3855 for s in subtree_parents {
3856 parents_that_need_to_reposition_children_x.insert(s);
3857 }
3858 }
3859
3860 for n in parents_that_need_to_reposition_children_y.clone() {
3861 let subtree_parents = layout_result.styled_dom.get_subtree_parents(n);
3862 for s in subtree_parents {
3863 parents_that_need_to_reposition_children_y.insert(s);
3864 }
3865 }
3866
3867 width_calculated_rect_arena_apply_flex_grow(
3870 &mut layout_result.width_calculated_rects,
3871 &layout_result.styled_dom.node_hierarchy.as_container(),
3872 &layout_result.layout_displays.as_ref(),
3873 &layout_result.layout_flex_grows.as_ref(),
3874 &layout_result.layout_positions.as_ref(),
3875 &layout_result.layout_flex_directions.as_ref(),
3876 &layout_result.styled_dom.non_leaf_nodes.as_ref(),
3877 root_size.width as f32,
3878 &parents_that_need_to_recalc_width_of_children,
3880 );
3881
3882 height_calculated_rect_arena_apply_flex_grow(
3883 &mut layout_result.height_calculated_rects,
3884 &layout_result.styled_dom.node_hierarchy.as_container(),
3885 &layout_result.layout_displays.as_ref(),
3886 &layout_result.layout_flex_grows.as_ref(),
3887 &layout_result.layout_positions.as_ref(),
3888 &layout_result.layout_flex_directions.as_ref(),
3889 &layout_result.styled_dom.non_leaf_nodes.as_ref(),
3890 root_size.height as f32,
3891 &parents_that_need_to_recalc_height_of_children,
3893 );
3894
3895 get_x_positions(
3898 &mut layout_result.solved_pos_x,
3899 &layout_result.width_calculated_rects.as_ref(),
3900 &layout_result.styled_dom.node_hierarchy.as_container(),
3901 &layout_result.layout_positions.as_ref(),
3902 &layout_result.layout_flex_directions.as_ref(),
3903 &layout_result.layout_justify_contents.as_ref(),
3904 &layout_result.styled_dom.non_leaf_nodes.as_ref(),
3905 LogicalPosition::new(root_bounds.origin.x as f32, root_bounds.origin.y as f32),
3906 &parents_that_need_to_reposition_children_x, );
3908
3909 get_y_positions(
3910 &mut layout_result.solved_pos_y,
3911 &layout_result.height_calculated_rects.as_ref(),
3912 &layout_result.styled_dom.node_hierarchy.as_container(),
3913 &layout_result.layout_positions.as_ref(),
3914 &layout_result.layout_flex_directions.as_ref(),
3915 &layout_result.layout_justify_contents.as_ref(),
3916 &layout_result.styled_dom.non_leaf_nodes.as_ref(),
3917 LogicalPosition::new(root_bounds.origin.x as f32, root_bounds.origin.y as f32),
3918 &parents_that_need_to_reposition_children_y, );
3920
3921 let mut updated_word_caches = parents_that_need_to_recalc_width_of_children.clone();
3923 for parent_id in parents_that_need_to_recalc_width_of_children.iter() {
3924 for child_id in
3925 parent_id.az_children(&layout_result.styled_dom.node_hierarchy.as_container())
3926 {
3927 updated_word_caches.insert(child_id);
3929 }
3930 }
3931
3932 updated_word_caches.extend(node_ids_that_changed_text_content.clone().into_iter());
3933
3934 #[cfg(feature = "text_layout")]
3935 create_word_positions(
3936 &mut layout_result.positioned_words_cache,
3937 &updated_word_caches,
3938 renderer_resources,
3939 &layout_result.words_cache,
3940 &layout_result.shaped_words_cache,
3941 &layout_result.styled_dom,
3942 Some(&layout_result.width_calculated_rects.as_ref()),
3943 );
3944
3945 let mut nodes_that_changed_size = BTreeSet::new();
3947 for parent_id in parents_that_need_to_recalc_width_of_children {
3948 nodes_that_changed_size.insert(parent_id);
3949 for child_id in
3950 parent_id.az_children(&layout_result.styled_dom.node_hierarchy.as_container())
3951 {
3952 nodes_that_changed_size.insert(child_id);
3953 }
3954 }
3955 for parent_id in parents_that_need_to_recalc_height_of_children {
3956 nodes_that_changed_size.insert(parent_id);
3957 for child_id in
3958 parent_id.az_children(&layout_result.styled_dom.node_hierarchy.as_container())
3959 {
3960 nodes_that_changed_size.insert(child_id);
3961 }
3962 }
3963 for node_text_content_changed in &node_ids_that_changed_text_content {
3964 let parent = layout_result.styled_dom.node_hierarchy.as_container()
3965 [*node_text_content_changed]
3966 .parent_id()
3967 .unwrap_or(NodeId::ZERO);
3968 nodes_that_changed_size.insert(parent);
3969 }
3970 nodes_that_changed_size.extend(node_ids_that_changed_text_content.into_iter());
3971
3972 let css_property_cache = layout_result.styled_dom.get_css_property_cache();
3973 let node_data_container = layout_result.styled_dom.node_data.as_container();
3974
3975 let mut all_offsets_to_recalc = BTreeMap::new();
3976 for node_id in nodes_that_changed_size.iter() {
3977 all_offsets_to_recalc.entry(*node_id).or_insert_with(|| {
3978 let styled_node_state =
3979 &layout_result.styled_dom.styled_nodes.as_container()[*node_id].state;
3980 precalculate_offset(
3981 &node_data_container[*node_id],
3982 &css_property_cache,
3983 node_id,
3984 styled_node_state,
3985 )
3986 });
3987
3988 for child_id in node_id.az_children(&layout_result.styled_dom.node_hierarchy.as_container())
3989 {
3990 all_offsets_to_recalc.entry(child_id).or_insert_with(|| {
3991 let styled_node_state =
3992 &layout_result.styled_dom.styled_nodes.as_container()[child_id].state;
3993 precalculate_offset(
3994 &node_data_container[*node_id],
3995 &css_property_cache,
3996 &child_id,
3997 styled_node_state,
3998 )
3999 });
4000 }
4001 }
4002
4003 position_nodes(
4006 &mut layout_result.rects.as_ref_mut(),
4007 &layout_result.styled_dom,
4008 AllOffsetsProvider::OnlyRecalculatedNodes(&all_offsets_to_recalc),
4009 &layout_result.width_calculated_rects.as_ref(),
4010 &layout_result.height_calculated_rects.as_ref(),
4011 &layout_result.solved_pos_x.as_ref(),
4012 &layout_result.solved_pos_y.as_ref(),
4013 &nodes_that_changed_size,
4014 &nodes_that_changed_size,
4015 &layout_result.layout_positions.as_ref(),
4016 &layout_result.words_cache,
4017 &layout_result.shaped_words_cache,
4018 &layout_result.positioned_words_cache,
4019 document_id,
4020 );
4021
4022 layout_result.root_size = root_bounds.size;
4023 layout_result.root_position = root_bounds.origin;
4024
4025 if !nodes_that_changed_size.is_empty() {
4026 get_nodes_that_need_scroll_clip(
4028 &mut layout_result.scrollable_nodes,
4029 &layout_result.styled_dom.styled_nodes.as_container(),
4030 &layout_result.styled_dom.node_data.as_container(),
4031 &layout_result.styled_dom.node_hierarchy.as_container(),
4032 &layout_result.rects.as_ref(),
4033 &layout_result.styled_dom.non_leaf_nodes.as_ref(),
4034 dom_id,
4035 document_id,
4036 );
4037 }
4038
4039 let gpu_key_changes = layout_result
4040 .gpu_value_cache
4041 .synchronize(&layout_result.rects.as_ref(), &layout_result.styled_dom);
4042
4043 let resized_nodes = nodes_that_changed_size.into_iter().collect();
4044
4045 RelayoutChanges {
4046 resized_nodes,
4047 gpu_key_changes,
4048 }
4049}