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.
}
}
}