azul_layout/
solver.rs

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        /// - `preferred_inner_width` denotes the preferred width of the
134        /// width or height got from the from the rectangles content.
135        ///
136        /// For example, if you have an image, the `preferred_inner_width` is the images width,
137        /// if the node type is an text, the `preferred_inner_width` is the text height.
138        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                // ignore preferred_width if the width is set manually
162                WhConstraint::EqualTo(
163                    width
164                        .min(max_width.unwrap_or(f32::MAX))
165                        .max(min_width.unwrap_or(0.0)),
166                )
167            } else {
168                // no width, only min_width and max_width
169                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                    // no width or max_width, only min_width
179                    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                        // no width, min_width or max_width: try preferred width
190                        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
221// Returns the preferred width, given [width, min_width, max_width] inside a RectLayout
222// or `None` if the height can't be determined from the node alone.
223//
224// fn determine_preferred_width(layout: &RectLayout) -> Option<f32>
225determine_preferred!(determine_preferred_width, width);
226
227// Returns the preferred height, given [height, min_height, max_height] inside a RectLayout
228// or `None` if the height can't be determined from the node alone.
229//
230// fn determine_preferred_height(layout: &RectLayout) -> Option<f32>
231determine_preferred!(determine_preferred_height, height);
232
233/// ```rust
234/// typed_arena!(
235///     WidthCalculatedRect,
236///     preferred_width,
237///     determine_preferred_width,
238///     get_horizontal_padding,
239///     get_flex_basis_horizontal,
240///     width_calculated_rect_arena_from_rect_layout_arena,
241///     bubble_preferred_widths_to_parents,
242///     width_calculated_rect_arena_apply_flex_grow,
243///     width_calculated_rect_arena_sum_children_flex_basis,
244///     Horizontal,
245/// )
246/// ```
247macro_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        /// Fill out the preferred width of all nodes.
271        ///
272        /// We could operate on the NodeDataContainer<StyledNode> directly,
273        /// but that makes testing very hard since we are only interested
274        /// in testing or touching the layout. So this makes the
275        /// calculation maybe a few microseconds slower, but gives better
276        /// testing capabilities
277        ///
278        /// NOTE: Later on, this could maybe be a NodeDataContainer<&'a RectLayout>.
279        #[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            // then calculate the widths again, but this time using the parent nodes
289            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                    // TODO: get the initial width of the rect content
333                    $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                        // TODO: get the initial width of the rect content
373                        $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        /// Bubble the inner sizes to their parents -  on any parent nodes, fill out
403        /// the width so that the `preferred_width` can contain the child nodes (if
404        /// that doesn't violate the constraints of the parent)
405        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            // Reverse, since we want to go from the inside out
415            // (depth 5 needs to be filled out first)
416            //
417            // Set the preferred_width of the parent nodes
418            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), // TODO: wrong
430                };
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                            // cross direction: take max flex basis of children
455                            children_flex_basis = children_flex_basis.max(flex_basis);
456                        }
457                    });
458
459                // if the children overflow, then the maximum width / height that can be
460                // bubbled is the max_height / max_width of the parent
461                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                // parent minimum width = children (including borders, padding + margin of children)
468                // PLUS padding (including borders) of parent
469                let parent_min_inner_size_px = children_inner_width
470                    + node_data[parent_id].$get_padding_fn(parent_parent_width);
471
472                // bubble the min_inner_size_px to the parent
473                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            // Now, the width of all elements should be filled,
479            // but they aren't flex-grown yet
480        }
481
482        /// Go from the root down and flex_grow the children if
483        /// needed - respects the `width`, `min_width` and `max_width`
484        /// properties
485        ///
486        /// The layout step doesn't account for the min_width
487        /// and max_width constraints, so we have to adjust them manually
488        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            /// Does the actual width layout, respects the `width`,
500            /// `min_width` and `max_width` properties as well as the
501            /// `flex_grow` factor. `flex_shrink` currently does nothing.
502            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                // The inner space of the parent node, without the padding
513                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                // 1. Set all child elements to their minimum required width or 0.0
527                // if there is no min width
528                let mut children_flex_grow = children
529                    .iter()
530                    .map(|child_id| {
531                        if layout_positions[*child_id] != LayoutPosition::Absolute {
532                            // so that node.min_width + node.flex_grow_px = exact_width
533                            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                            // `position: absolute` items don't take space away from their siblings,
548                            // rather they take the minimum needed space by
549                            // their content
550                            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                            // The absolute positioned node might have a max-width constraint, which
564                            // has a higher precedence than `top, bottom, left,
565                            // right`.
566                            let max_space_current_node = width_calculated_arena[*child_id]
567                                .$preferred_field
568                                .calculate_from_relative_parent(relative_parent_width);
569
570                            // expand so that node.min_inner_size_px + node.flex_grow_px =
571                            // max_space_current_node
572                            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                // 2. Calculate how much space has been taken up so far by the minimum width /
585                //    height Exclude position: absolute items from being added into the sum since
586                //    they are taken out of the regular layout flow
587                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                // all items are now expanded to their minimum width,
602                // calculate how much space is remaining
603                let mut space_available = parent_node_inner_width - space_taken_up;
604
605                if space_available <= 0.0 {
606                    // no space to distribute
607                    return children_flex_grow;
608                }
609
610                // The fixed-width items are now considered solved,
611                // so subtract them out of the width of the parent.
612
613                // Get the node ids that have to be expanded, exclude
614                // fixed-width and absolute childrens
615                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                    // In order to apply flex-grow correctly, we need the sum of
638                    // the flex-grow factors of all the variable-width children
639                    //
640                    // NOTE: variable_width_childs can change its length,
641                    // have to recalculate every loop!
642                    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                    // Grow all variable children by the same amount.
654                    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                                        // flex-growing will overflow max-width, record overflow and
674                                        // set
675                                        ((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                    // The inner space of the parent node, without the padding
717                    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(); // +
766                                                                                         // width_calculated_arena[*child_id].
767                                                                                         // $get_padding_fn(parent_node_inner_width);
768                                                                                         // + margin(child)
769
770                        let space_available = parent_node_inner_width - min_child_width;
771
772                        // If the min width of the cross axis is larger than the parent width,
773                        // overflow
774                        if space_available <= 0.0
775                            || layout_displays[*child_id]
776                                .get_property()
777                                .copied()
778                                .unwrap_or_default()
779                                != LayoutDisplay::Flex
780                        {
781                            // do not grow the item - no space to distribute
782                            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                            // flex_grow the item so that (space_available + node.flex_grow_px) =
792                            // preferred_width (= either max_width or parent_width)
793                            preferred_width - min_child_width
794                        }
795                    })
796                    .collect()
797            }
798
799            use azul_css::{LayoutAxis, LayoutPosition};
800
801            // Set the window width on the root node (since there is only one root node, we can
802            // calculate the `flex_grow_px` directly)
803            //
804            // Usually `top_level_flex_basis` is NOT 0.0, rather it's the sum of all widths in the
805            // DOM, i.e. the sum of the whole DOM tree
806            let top_level_flex_basis = node_data.as_ref()[NodeId::ZERO].min_inner_size_px;
807
808            // The root node can still have some sort of max-width attached, so we need to check for
809            // that
810            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                // reset the flex_grow to 0
838                {
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                // calculate the new flex_grow
848                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                // write the new flex-grow values into the flex_grow_px
882                {
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
944/// Returns the solved widths of the items in a BTree form
945pub(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
979/// Returns the solved height of the items in a BTree form
980pub(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        /// Traverses along the DOM and solve for the X or Y position
1029        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            /// Returns the absolute X for the child
1040            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                    // align right / bottom of last relative parent
1063                    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                    // align left / top of last relative parent
1085                    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            // Returns the X for the child + the distance to add for the next child
1101            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                // total width of the child, including padding + border
1114                let child_width_with_padding = solved_widths[child_id].total();
1115
1116                // width: increase X according to the main axis, Y according to the cross_axis
1117                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                    // X position of the top left corner
1139                    // WARNING: End has to be added after all children!
1140                    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 // TODO!
1155                        }
1156                        SpaceAround => {
1157                            parent_x_position // TODO!
1158                        }
1159                        SpaceEvenly => {
1160                            parent_x_position // TODO!
1161                        }
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                    // Along main axis: Increase X with width of current element
1227                    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                    // If the direction is `flex-end`, we can't add the X position during the
1263                    // iteration, so we have to "add" the diff to the parent_inner_width
1264                    // at the end
1265                    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                    // Along cross axis: Take X of parent
1280
1281                    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    // Add the origin on top of the position
1347    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    // Add the origin on top of the position
1389    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()); // elide bounds checking
1400    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()); // elide bounds checking
1429
1430    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()); // elide bounds checking
1459
1460    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    // Prevent flex-grow and flex-shrink to be less than 0
1484    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()); // elide bounds checking
1488
1489    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    // Prevent flex-grow and flex-shrink to be less than 0
1514    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()); // elide bounds checking
1518
1519    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()); // elide bounds check
1543
1544    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// Adds the image and font resources to the app_resources but does NOT add them to the RenderAPI
1780#[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                // Generate a new DomID
1847                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                // Invoke the IFrame callback
1864                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                        // TODO - see /examples/assets/images/scrollbounds.png for documentation!
1871                        /* scroll_size */
1872                        bounds.size,
1873                        /* scroll_offset */ LogicalPosition::zero(),
1874                        /* virtual_scroll_size */ bounds.size,
1875                        /* virtual_scroll_offset */ 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                // layout_result.scrollable_nodes.get();
1903                // parent_dom_id
1904
1905                // TODO: use other fields of iframe_return here!
1906
1907                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                // TODO: use the iframe static position here?
1935                let bounds =
1936                    LogicalRect::new(LogicalPosition::zero(), hidpi_bounds.get_logical_size());
1937
1938                // push the styled iframe dom into the next iframes and repeat (recurse)
1939                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/// At this point in time, all font keys, image keys, etc. have to be already
1982/// been submitted to the RenderApi and the AppResources!
1983#[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    // TODO: Filter all inline text blocks: inline blocks + their padding + margin
1998    // The NodeId has to be the **next** NodeId (the next sibling after the inline element)
1999    // let mut inline_text_blocks = BTreeMap::<NodeId, InlineText>::new();
2000
2001    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    // Break all strings into words and / or resolve the TextIds
2016    let word_cache = create_word_cache(&styled_dom.node_data.as_container());
2017    // Scale the words to the correct size - TODO: Cache this in the app_resources!
2018    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    // same as all_nodes_btreeset, but only for the words
2025    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]) // if the word block is marked as display:none, ignore
2031        .map(|n| NodeId::new(n)).collect::<BTreeSet<_>>();
2032
2033    // Layout all words as if there was no max-width constraint
2034    // (to get the texts "content width").
2035    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    // Calculate the optional "intrinsic content widths" - i.e.
2047    // the width of a text or image, if no constraint would apply
2048    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    // If the flex grow / max-width step has caused the text block
2103    // to shrink in width, it needs to recalculate its height
2104    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            // if the text content overflows the parent width, we also need to recalculate
2115            } 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    // Recalculate the height of the content blocks for the word blocks that need it
2126    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    // Calculate the content height of the (text / image) content based on its width
2138    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            // preserve aspect ratio
2165            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    // TODO: The content height is not the final height!
2172    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
2306/// resets the preferred width / height to 0px before the layout is calculate
2307fn 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            // set all children as display:none
2327            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
2341/// Note: because this function is called both on layout() and relayout(),
2342/// the offsets are calculated during the layout() run. However,
2343/// we don't want to store all offsets because that would waste memory
2344///
2345/// So you can EITHER specify all offsets (useful during layout()) or specify
2346/// only the offsets of nodes that need to be recalculated (useful during relayout())
2347///
2348/// If an offset isn't found (usually shouldn't happen), the final positioned
2349/// rectangle is not positioned.
2350enum 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    // create the final positioned rectangles
2388    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                // calculate relative to parent
2420                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                // calculate relative to parent
2427                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                // calculate relative to last positioned item
2434                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                // relative to screen, already done
2441                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        // push positioned item and layout children
2465        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            // copy the width and height from the parent node
2471            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                    // calculate relative to parent
2487                    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                    // calculate relative to parent
2494                    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                    // calculate relative to last positioned item
2501                    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                    // relative to screen, already done
2508                    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            // set text, if any
2532            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        // NOTE: Intentionally do not set text_layout_options,
2622        // otherwise this would overwrite the existing text layout options
2623        // Label / Text nodes are ALWAYS children of some parent node,
2624        // they can not be root nodes. Therefore the children_iter() will take
2625        // care of layouting the text
2626        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// same as get_inline_text(), but shapes a new word instead of using the internal one
2667// - necessary to implement text cursor, so that we can calculate the x-offset of
2668// the text cursor for the next frame (after the character has been pressed)
2669#[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            // downcast the loaded_font.font from *const c_void to *const ParsedFont
2712            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(),     // TODO
2823                holes: Vec::new().into(), // TODO
2824                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
2842/// For a given rectangle, determines what text alignment should be used
2843fn determine_text_alignment(
2844    align_items: LayoutAlignItems,
2845    justify_content: LayoutJustifyContent,
2846    text_align: Option<CssPropertyValue<StyleTextAlign>>,
2847) -> (StyleTextAlign, StyleVerticalAlign) {
2848    // Vertical text alignment
2849    let vert_alignment = match align_items {
2850        LayoutAlignItems::FlexStart => StyleVerticalAlign::Top,
2851        LayoutAlignItems::FlexEnd => StyleVerticalAlign::Bottom,
2852        // technically stretch = blocktext, but we don't have that yet
2853        _ => StyleVerticalAlign::Center,
2854    };
2855
2856    // Horizontal text alignment
2857    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        // Horizontal text alignment with higher priority
2868        horz_alignment = text_align;
2869    }
2870
2871    (horz_alignment, vert_alignment)
2872}
2873
2874/// Returns all node IDs where the children overflow the parent, together with the
2875/// `(parent_rect, child_rect)` - the child rect is the sum of the children.
2876///
2877/// TODO: The performance of this function can be theoretically improved:
2878///
2879/// - Unioning the rectangles is heavier than just looping through the children and
2880/// summing up their width / height / padding + margin.
2881/// - Scroll nodes only need to be inserted if the parent doesn't have `overflow: hidden`
2882/// activated
2883/// - Overflow for X and Y needs to be tracked seperately (for overflow-x / overflow-y separation),
2884/// so there we'd need to track in which direction the inner_rect is overflowing.
2885fn 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    // brute force: calculate all immediate children sum rects of all parents
2906    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            // cannot create scroll clips if the parent is less than 1px wide
2913            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            // only register the directly overflowing children
2931            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    // Go from the inside out and bubble the overflowing rectangles
2940    // based on the overflow-x / overflow-y property
2941    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                // modify the rect in the all_direct_overflows,
2972                // then recalculate the rectangles for all parents
2973                // this is expensive, but at least correct
2974            }
2975        }
2976    }
2977
2978    // Insert all rectangles that need to scroll
2979    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
3036/// Relayout function, takes an existing LayoutResult and adjusts it
3037/// so that only the nodes that need relayout are touched.
3038/// See `CallbacksToCall`
3039///
3040/// Returns a vec of node IDs that whose layout was changed
3041pub 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    // shortcut: in most cases, the root size hasn't
3052    // changed and there are no nodes to relayout
3053
3054    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    // merge the nodes to relayout by type so that we don't relayout twice
3062    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    // ---- step 1: recalc size
3096
3097    // TODO: for now, the preferred_widths and preferred_widths is always None,
3098    // so the content width + height isn't taken into account. If that changes,
3099    // the new content size has to be calculated first!
3100
3101    // TODO: changes to display, float and box-sizing property are ignored
3102    // TODO: changes to top, bottom, right, left property are ignored for now
3103    // TODO: changes to position: property are updated, but ignored for now
3104
3105    // recalc(&mut layout_result.preferred_widths);
3106
3107    let mut display_changed = false;
3108
3109    // update the precalculated properties (position, flex-grow,
3110    // flex-direction, justify-content)
3111    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    /*
3177    if display_changed {
3178        // recalculate changed display:none nodes
3179        let new_display_none_nodes = get_display_none_nodes(
3180            &styled_dom.node_hierarchy.as_container(),
3181            &layout_result.layout_displays.as_ref(),
3182        );
3183    }
3184    */
3185
3186    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    // Update words cache and shaped words cache
3212    #[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    // parents need to be adjust before children
3331    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                        // recalculate min / max / preferred width constraint if needed
3378                        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                        // recalculate min / max / preferred width constraint if needed
3437                        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                        // padding / margin horizontal change
3496                        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                        // padding / margin vertical change
3529                        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                            // need to recalc the width of the node
3565                            // need to bubble the width to the parent width
3566                            // need to recalc the width of all children
3567                            // need to recalc the x position of all siblings
3568                            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                            // need to recalc the height of the node
3581                            // need to bubble the height of all current node siblings to the parent
3582                            // height need to recalc the height of all children
3583                            // need to recalc the y position of all siblings
3584                            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                            // need to recalc the widths of all children
3595                            // need to recalc the x position of all children
3596                            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                            // need to recalc the heights of all children
3602                            // need to bubble the height of all current node children to the
3603                            // current node min_inner_size_px
3604                            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                            // need to recalc the widths of all siblings
3610                            // need to recalc the x positions of all siblings
3611                            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                            // need to recalc the heights of all siblings
3617                            // need to recalc the y positions of all siblings
3618                            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                        // TODO: absolute positions / top-left-right-bottom changes!
3623                    }
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    // for all nodes that changed, recalculate the min_inner_size_px of the parents
3646    // by re-bubbling the sizes to the parents (but only for the nodes that need it)
3647    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    // propagate width / height change from the inside out
3698    while !parents_that_need_to_recalc_width_of_children.is_empty() {
3699        // width_calculated_rect_arena_apply_flex_grow also
3700        // needs the parents parent to work correctly
3701        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            // important - only recalc the widths necessary!
3739            &parents_that_need_to_recalc_width_of_children,
3740        );
3741
3742        // if the parent width is not the same, bubble
3743        let parents_that_changed_width = parents_that_need_to_recalc_width_of_children
3744            .iter()
3745            .filter_map(|p| {
3746                // get the current width after relayout
3747                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        // loop while there are still widths that changed size
3763        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            // important - only recalc the heights necessary!
3807            &parents_that_need_to_recalc_height_of_children,
3808        );
3809
3810        // if the parent height is not the same, bubble
3811        let mut parents_that_changed_height = parents_that_need_to_recalc_height_of_children
3812            .iter()
3813            .filter_map(|p| {
3814                // get the current height after relayout
3815                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        // loop while there are still heights that changed size
3831        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    // if a node has been modified then the entire subtree needs to be re-laid out
3837    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    // -- step 2: recalc position for those parents that need it
3868
3869    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        // important - only recalc the widths necessary!
3879        &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        // important - only recalc the heights necessary!
3892        &parents_that_need_to_recalc_height_of_children,
3893    );
3894
3895    // -- step 2: recalc position for those parents that need it
3896
3897    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, // <- important
3907    );
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, // <- important
3919    );
3920
3921    // update positioned_word_cache
3922    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            // if max_width_changed { } // - optimization?
3928            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    // determine which nodes changed their size and return
3946    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    // update layout_result.rects and layout_result.glyph_cache
4004    // if positioned_word_cache changed, regenerate layouted_glyph_cache
4005    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        // TODO: optimize?
4027        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}