fn flex_lines_main_size()

in GaiaXStretch/src/algo.rs [1275:1596]


    fn flex_lines_main_size(
        &mut self,
        parent_node_size: Size<Number>,
        parent_node_dir: FlexDirection,
        parent_node_is_row: bool,
        parent_node_is_column: bool,
        parent_node_inner_size: Size<Number>,
        parent_node_available_space: Size<Number>,
        flex_lines: &mut sys::Vec<FlexLine>,
    ) {
        for line in &mut flex_lines[..] {
            // 1. Determine the used flex factor. Sum the outer hypothetical main sizes of all
            //    items on the line. If the sum is less than the flex container’s inner main size,
            //    use the flex grow factor for the rest of this algorithm; otherwise, use the
            //    flex shrink factor.
            // 1. 确定使用的flex因子。
            //    将所有当前行的flex项目猜测的外部主轴尺寸相加。
            //    如果之和小于父节点flex容器的内部主轴尺寸大小,使用flex增长因子算法;否则,使用flex收缩因子算法。

            let line_used_flex_factor: f32 =
                line.items.iter().map(|child: &FlexItem| child.hypothetical_outer_size.main(parent_node_dir)).sum();
            let parent_node_inner_size_main = parent_node_inner_size.main(parent_node_dir).or_else(0.0);
            let line_of_growing = line_used_flex_factor < parent_node_inner_size_main;
            let line_of_shrinking = !line_of_growing;

            // 2. Size inflexible items. Freeze, setting its target main size to its hypothetical main size
            //    - Any item that has a flex factor of zero
            //    - If using the flex grow factor: any item that has a flex base size
            //      greater than its hypothetical main size
            //    - If using the flex shrink factor: any item that has a flex base size
            //      smaller than its hypothetical main size
            // 2. 对于无法伸缩的flex项目。冻结住,设置flex项目的主轴尺寸就是其猜测的主轴尺寸。
            //    - flex因子为零的任何项
            //    - 如果使用flex增长因子: 任何具有基准大小的flex项目比其猜测的主轴尺寸更大
            //    - 如果使用flex收缩因子: 任何具有基准大小的flex项目比其猜测的主轴尺寸更小

            for target in line.items.iter_mut() {
                let child: &mut FlexItem = target;
                // TODO - This is not found by reading the spec. Maybe this can be done in some other place
                // instead. This was found by trail and error fixing tests to align with webkit output.
                // 如果父节点是横向排列 && 父节点的主轴尺寸未定义
                // 需要根据孩子节点的尺寸计算的结果当做flex项目的主轴尺寸
                if parent_node_is_row && parent_node_inner_size.main(parent_node_dir).is_undefined() {
                    let child_target_node_size = Size {
                        width: child.size.width.maybe_max(child.min_size.width).maybe_min(child.max_size.width),
                        height: child.size.height.maybe_max(child.min_size.height).maybe_min(child.max_size.height),
                    };
                    let child_target_main_size = self
                        .compute_internal(child.node, child_target_node_size, parent_node_available_space, false)
                        .size
                        .main(parent_node_dir)
                        .maybe_max(child.min_size.main(parent_node_dir))
                        .maybe_min(child.max_size.main(parent_node_dir));
                    child.target_size.set_main(parent_node_dir, child_target_main_size);
                }
                // 使用猜测的内部主轴尺寸作为flex项目的主轴尺寸
                else {
                    let target_size_main = child.hypothetical_inner_size.main(parent_node_dir);
                    child.target_size.set_main(parent_node_dir, target_size_main);
                }

                // TODO this should really only be set inside the if-statement below but
                // that causes the target_main_size to never be set for some items

                // 使用flex项目的主轴尺寸加上外边距,作为flex项目的外部主轴尺寸
                let child_out_target_size_main =
                    child.target_size.main(parent_node_dir) + child.margin.main(parent_node_dir);
                child.outer_target_size.set_main(parent_node_dir, child_out_target_size_main);

                // 判定是否需要冻结孩子节点
                let child_style: &Style = &self.nodes[child.node].style;
                let child_is_no_grow_and_no_shrink = child_style.flex_grow == 0.0 && child_style.flex_shrink == 0.0;
                let child_is_no_grow =
                    line_of_growing && child.flex_basis > child.hypothetical_inner_size.main(parent_node_dir);
                let child_is_no_shrink =
                    line_of_shrinking && child.flex_basis < child.hypothetical_inner_size.main(parent_node_dir);

                if child_is_no_grow_and_no_shrink || child_is_no_grow || child_is_no_shrink {
                    child.frozen = true;
                }
            }

            // 3. Calculate initial free space. Sum the outer sizes of all items on the line,
            //    and subtract this from the flex container’s inner main size. For frozen items,
            //    use their outer target main size; for other items, use their outer flex base size.

            // 3. 计算已用空间,将这一行所有的flex项目外部主轴尺寸相加。
            let line_of_used_space: f32 = line
                .items
                .iter()
                .map(|child: &FlexItem| {
                    child.margin.main(parent_node_dir)
                        + if child.frozen { child.target_size.main(parent_node_dir) } else { child.flex_basis }
                })
                .sum();

            // 计算初始可用空间。将这一行所有的flex项目外部主轴尺寸相加,并从父节点flex容器的内部主轴大小中减去这个值。
            let line_of_initial_free_space =
                (parent_node_inner_size.main(parent_node_dir) - line_of_used_space).or_else(0.0);

            // 4. Loop

            loop {
                // a. Check for flexible items. If all the flex items on the line are frozen,
                //    free space has been distributed; exit this loop.

                // 检查当前flex项目行中的所有元素是否都被冻结,如果都被冻结,代表剩余可用空间都已被分发完。
                // 那么就退出循环
                let all_frozen = line.items.iter().all(|child: &FlexItem| child.frozen);
                if all_frozen {
                    break;
                }

                // b. Calculate the remaining free space as for initial free space, above.
                //    If the sum of the unfrozen flex items’ flex factors is less than one,
                //    multiply the initial free space by this sum. If the magnitude of this
                //    value is less than the magnitude of the remaining free space, use this
                //    as the remaining free space.
                // 使用上面计算初始可用空间去计算的剩余可用空间。
                // 如果未冻结的flex项目的的flex因子之和小于1,将初始剩余空间乘以这个和。如果这个的值小于剩余可用空间的大小,使用此值作为剩余可用空间。

                // 已使用的空间
                let used_space: f32 = line
                    .items
                    .iter()
                    .map(|child: &FlexItem| {
                        child.margin.main(parent_node_dir)
                            + if child.frozen { child.target_size.main(parent_node_dir) } else { child.flex_basis }
                    })
                    .sum();

                // 收集所有未冻结的flex项目
                let mut unfrozen: sys::Vec<&mut FlexItem> =
                    line.items.iter_mut().filter(|child| !child.frozen).collect();

                // 计算所有的增长因子之和和所有收缩因子之和
                let (sum_flex_grow, sum_flex_shrink): (f32, f32) =
                    unfrozen.iter().fold((0.0, 0.0), |(flex_grow, flex_shrink), item| {
                        let style: &Style = &self.nodes[item.node].style;
                        (flex_grow + style.flex_grow, flex_shrink + style.flex_shrink)
                    });

                // 剩余空间
                let free_space: f32 =
                    // 使用增长因子,如果当前行的元素增长只和小于1.0
                    //
                    if line_of_growing && sum_flex_grow < 1.0 {
                        (line_of_initial_free_space * sum_flex_grow).maybe_min(parent_node_inner_size.main(parent_node_dir) - used_space)
                    }
                    // 使用收缩银子,如果当前行的元素收缩只和小于1.0
                    else if line_of_shrinking && sum_flex_shrink < 1.0 {
                        (line_of_initial_free_space * sum_flex_shrink).maybe_max(parent_node_inner_size.main(parent_node_dir) - used_space)
                    }
                    // 使用父节点的内部主轴尺寸减去当前行已使用空间,作为剩余空间
                    else {
                        (parent_node_inner_size.main(parent_node_dir) - used_space).or_else(0.0)
                    };

                // c. Distribute free space proportional to the flex factors.
                //    - If the remaining free space is zero
                //        Do Nothing
                //    - If using the flex grow factor
                //        Find the ratio of the item’s flex grow factor to the sum of the
                //        flex grow factors of all unfrozen items on the line. Set the item’s
                //        target main size to its flex base size plus a fraction of the remaining
                //        free space proportional to the ratio.
                //    - If using the flex shrink factor
                //        For every unfrozen item on the line, multiply its flex shrink factor by
                //        its inner flex base size, and note this as its scaled flex shrink factor.
                //        Find the ratio of the item’s scaled flex shrink factor to the sum of the
                //        scaled flex shrink factors of all unfrozen items on the line. Set the item’s
                //        target main size to its flex base size minus a fraction of the absolute value
                //        of the remaining free space proportional to the ratio. Note this may result
                //        in a negative inner main size; it will be corrected in the next step.
                //    - Otherwise
                //        Do Nothing

                // 根据flex因子分发剩余空间。
                // 1. 如果剩余空间是0,什么也不做。
                // 2. 如果使用flex增长因子
                //      计算flex项目的增长比例系数,并结合剩余空间计算出flex项目的增长值,加上flex项目的基准值,作为flex项目的主轴尺寸。
                //      flex项目增长的长度与flex增长数值成正比关系。
                // 3. 如果使用flex收缩银子
                //      计算flex项目的收缩比例系数,并结合剩余空间计算出flex项目的收缩值,加上flex项目的基准值,作为flex项目的主轴尺寸。
                //      flex项目收缩的长度与flex收缩数值成正比关系。

                if line_of_growing {
                    for target in &mut unfrozen {
                        let child: &mut FlexItem = target;
                        if free_space.is_normal() && sum_flex_grow > 0.0 {
                            let grow_after = child.flex_basis
                                + free_space * (self.nodes[child.node].style.flex_grow / sum_flex_grow);
                            child.target_size.set_main(parent_node_dir, grow_after);
                        }
                    }
                } else if line_of_shrinking && sum_flex_shrink > 0.0 {
                    let sum_scaled_shrink_factor: f32 = unfrozen
                        .iter()
                        .map(|child: &&mut FlexItem| {
                            let child_style: Style = self.nodes[child.node].style;
                            child.inner_flex_basis * child_style.flex_shrink
                        })
                        .sum();

                    for target in &mut unfrozen {
                        let child: &mut FlexItem = target;
                        let scaled_shrink_factor = child.inner_flex_basis * self.nodes[child.node].style.flex_shrink;

                        if free_space.is_normal() && sum_scaled_shrink_factor > 0.0 {
                            let shrink_after =
                                child.flex_basis + free_space * (scaled_shrink_factor / sum_scaled_shrink_factor);
                            child.target_size.set_main(parent_node_dir, shrink_after);
                        } else {
                            let child_style: &Style = &self.nodes[child.node].style;
                            if parent_node_is_column && child_style.aspect_ratio.is_defined() {
                                // fix: aspect_ratio_nest_flex_grow_flex_direction_column
                                // 这种情况下,在纵轴撑满剩余空间,实际上要从宽度的角度计算高度
                                if parent_node_inner_size.cross(parent_node_dir).is_defined()
                                    && !child_style.size.width.is_defined()
                                    && child_style.flex_grow > 0.0
                                {
                                    // fix: aspect_ratio_nest_flex_grow_flex_direction_column_multi_with_margin
                                    // 处理margin情况
                                    let margin: Rect<f32> =
                                        child_style.margin.map(|n| n.resolve(parent_node_size.width).or_else(0.0));

                                    // fix: aspect_ratio_nest_flex_grow_flex_direction_column_multi
                                    // 将整个宽度作为剩余空间计算
                                    let free_space = parent_node_inner_size.cross(parent_node_dir).or_else(0.0)
                                        - margin.horizontal();
                                    let height = free_space / child_style.aspect_ratio.or_else(0.0);
                                    child.target_size.set_main(parent_node_dir, height);
                                }
                            }
                        }
                    }
                }

                // d. Fix min/max violations.
                //    Clamp each non-frozen item’s target main size by its used min and max main sizes and floor its content-box size at zero.
                //    If the item’s target main size was made smaller by this, it’s a max violation.
                //    If the item’s target main size was made larger by this, it’s a min violation.

                let total_violation = unfrozen.iter_mut().fold(0.0, |acc: f32, child: &mut &mut FlexItem| -> f32 {
                    // TODO - not really spec abiding but needs to be done somewhere. probably somewhere else though.
                    // The following logic was developed not from the spec but by trail and error looking into how
                    // webkit handled various scenarios. Can probably be solved better by passing in
                    // min-content max-content constraints from the top. Need to figure out correct thing to do here as
                    // just piling on more conditionals.
                    let child_node = child.node;
                    let child_style: Style = self.nodes[child_node].style;

                    let child_node_target_size = child.target_size.map(|s| s.into());

                    let min_main = if parent_node_is_row && self.nodes[child_node].measure.is_none() {
                       
                        // 在Column和Row相互嵌套且有复杂的自适应和压缩状态时
                        // 需要特殊处理一些情况
                        if (child_style.flex_direction == FlexDirection::Column
                            || child_style.flex_direction == FlexDirection::ColumnReverse)
                            && !child_style.flex_basis.is_defined()
                            && !child_style.aspect_ratio.is_defined()
                            && child_style.flex_shrink == 1.0
                            && child_style.flex_grow == 1.0
                        {
                            let child_size = self
                                .compute_internal(
                                    child_node,
                                    child_node_target_size,
                                    parent_node_available_space,
                                    false,
                                )
                                .size;

                            child_size.width.maybe_min(child.size.width).maybe_max(child.min_size.width).into()
                        } else {
                            let child_size = self
                                .compute_internal(child_node, Size::undefined(), parent_node_available_space, false)
                                .size;

                            child_size.width.maybe_min(child.size.width).maybe_max(child.min_size.width).into()
                        }
                    } else {
                        child.min_size.main(parent_node_dir)
                    };

                    let max_main = child.max_size.main(parent_node_dir);

                    let clamped =
                        child.target_size.main(parent_node_dir).maybe_min(max_main).maybe_max(min_main).max(0.0);

                    child.violation = clamped - child.target_size.main(parent_node_dir);

                    child.target_size.set_main(parent_node_dir, clamped);
                    let outer_main = child.target_size.main(parent_node_dir) + child.margin.main(parent_node_dir);
                    child.outer_target_size.set_main(parent_node_dir, outer_main);

                    acc + child.violation
                });

                // e. Freeze over-flexed items. The total violation is the sum of the adjustments
                //    from the previous step ∑(clamped size - unclamped size). If the total violation is:
                //    - Zero
                //        Freeze all items.
                //    - Positive
                //        Freeze all the items with min violations.
                //    - Negative
                //        Freeze all the items with max violations.

                for target in &mut unfrozen {
                    let child: &mut &mut FlexItem = target;
                    match total_violation {
                        v if v > 0.0 => child.frozen = child.violation > 0.0,
                        v if v < 0.0 => child.frozen = child.violation < 0.0,
                        _ => child.frozen = true,
                    }
                }

                // f. Return to the start of this loop.
            }
        }
    }