Add FlexLine and its method for flex resolving

Add the 'FlexLine' struct to represent a line in flex container, and the
'flex_resolve()' method for flex lengths resolving.
This commit is contained in:
Pu Xingyu 2016-07-20 16:41:17 +08:00
parent bf5805f2a6
commit 87a041d416

View file

@ -24,6 +24,7 @@ use model::{IntrinsicISizes, MaybeAuto, MinMaxConstraint};
use model::{specified, specified_or_none};
use script_layout_interface::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW};
use std::cmp::{max, min};
use std::ops::Range;
use std::sync::Arc;
use style::computed_values::flex_direction;
use style::computed_values::{box_sizing, border_collapse};
@ -262,6 +263,98 @@ impl FlexItem {
}
}
/// A line in a flex container.
// TODO(stshine): More fields are required to handle collapsed items and baseline alignment.
#[derive(Debug)]
struct FlexLine {
/// Range of items belong to this line in 'self.items'.
pub range: Range<usize>,
/// Remainig free space of this line, items will grow or shrink based on it being positive or negative.
pub free_space: Au,
/// the number of auto margins of items.
pub auto_margin_count: i32,
/// Line size in the block direction.
pub cross_size: Au,
}
impl FlexLine {
pub fn new(range: Range<usize>, free_space: Au, auto_margin_count: i32) -> FlexLine {
FlexLine {
range: range,
auto_margin_count: auto_margin_count,
free_space: free_space,
cross_size: Au(0)
}
}
/// This method implements the flexible lengths resolving algorithm.
/// The 'collapse' parameter is used to indicate whether items with 'visibility: hidden'
/// is included in length resolving. The result main size is stored in 'item.main_size'.
/// https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths
pub fn flex_resolve(&mut self, items: &mut [FlexItem], collapse: bool) {
let mut total_grow = 0.0;
let mut total_shrink = 0.0;
let mut total_scaled = 0.0;
let mut active_count = 0;
// Iterate through items, collect total factors and freeze those that have already met
// their constraints or won't grow/shrink in corresponding scenario.
// https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths
for item in items.iter_mut().filter(|i| !(i.is_strut && collapse)) {
item.main_size = max(item.min_size, min(item.base_size, item.max_size));
if item.main_size != item.base_size
|| (self.free_space > Au(0) && item.flex_grow == 0.0)
|| (self.free_space < Au(0) && item.flex_shrink == 0.0) {
item.is_frozen = true;
} else {
item.is_frozen = false;
total_grow += item.flex_grow;
total_shrink += item.flex_shrink;
// The scaled factor is used to calculate flex shrink
total_scaled += item.flex_shrink * item.base_size.0 as f32;
active_count += 1;
}
}
let initial_free_space = self.free_space;
let mut total_variation = Au(1);
// If there is no remaining free space or all items are frozen, stop loop.
while total_variation != Au(0) && self.free_space != Au(0) && active_count > 0 {
self.free_space =
// https://drafts.csswg.org/css-flexbox/#remaining-free-space
if self.free_space > Au(0) {
min(initial_free_space.scale_by(total_grow), self.free_space)
} else {
max(initial_free_space.scale_by(total_shrink), self.free_space)
};
total_variation = Au(0);
for item in items.iter_mut().filter(|i| !i.is_frozen).filter(|i| !(i.is_strut && collapse)) {
// Use this and the 'abs()' below to make the code work in both grow and shrink scenarios.
let (factor, end_size) = if self.free_space > Au(0) {
(item.flex_grow / total_grow, item.max_size)
} else {
(item.flex_shrink * item.base_size.0 as f32 / total_scaled, item.min_size)
};
let variation = self.free_space.scale_by(factor);
if variation.0.abs() > (end_size - item.main_size).0.abs() {
// Use constraint as the target main size, and freeze item.
total_variation += end_size - item.main_size;
item.main_size = end_size;
item.is_frozen = true;
active_count -= 1;
total_shrink -= item.flex_shrink;
total_grow -= item.flex_grow;
total_scaled -= item.flex_shrink * item.base_size.0 as f32;
} else {
total_variation += variation;
item.main_size += variation;
}
}
self.free_space -= total_variation;
}
}
}
/// A block with the CSS `display` property equal to `flex`.
#[derive(Debug)]
pub struct FlexFlow {
@ -274,6 +367,8 @@ pub struct FlexFlow {
available_main_size: AxisSize,
/// The available cross axis size
available_cross_size: AxisSize,
/// List of flex lines in the container.
lines: Vec<FlexLine>,
/// List of flex-items that belong to this flex-container
items: Vec<FlexItem>,
/// True if the flex-direction is *-reversed
@ -318,6 +413,7 @@ impl FlexFlow {
main_mode: main_mode,
available_main_size: AxisSize::Infinite,
available_cross_size: AxisSize::Infinite,
lines: Vec::new(),
items: Vec::new(),
main_reverse: main_reverse,
is_wrappable: is_wrappable,