Delta semántico · 57
examples/basic.rs
- cambió por dentro fn main
scripts/gentest/src/main.rs
scripts/gentest/test_helper.js
15 archivos · +2303 −1589
diff --git a/.gitignore b/.gitignore
index 693699042..8144c2f5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
/target
+/scripts/gentest/target
+
**/*.rs.bk
Cargo.lock
+.DS_Store
diff --git a/examples/basic.rs b/examples/basic.rs
index 7289695d7..244bfca1c 100644
--- a/examples/basic.rs
+++ b/examples/basic.rs
@@ -6,13 +6,11 @@ fn main() {
height: stretch::style::Dimension::Points(100.0),
justify_content: stretch::style::JustifyContent::Center,
- children: vec![
- stretch::style::Node {
- width: stretch::style::Dimension::Percent(0.5),
+ children: vec![stretch::style::Node {
+ width: stretch::style::Dimension::Percent(0.5),
- ..Default::default()
- },
- ],
+ ..Default::default()
+ }],
..Default::default()
};
@@ -21,4 +19,3 @@ fn main() {
println!("{:#?}", layout);
}
-
diff --git a/scripts/gentest/Cargo.toml b/scripts/gentest/Cargo.toml
new file mode 100644
index 000000000..fe97b86b3
--- /dev/null
+++ b/scripts/gentest/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "gentest"
+version = "0.1.0"
+authors = ["Emil Sjölander <emil@visly.app>"]
+
+[dependencies]
+selenium-rs = "0.1.0"
+json = "0.11.13"
diff --git a/scripts/gentest/selenium.jar b/scripts/gentest/selenium.jar
new file mode 100755
index 000000000..8410e9565
Binary files /dev/null and b/scripts/gentest/selenium.jar differ
diff --git a/scripts/gentest/src/main.rs b/scripts/gentest/src/main.rs
new file mode 100644
index 000000000..f2a5f76c3
--- /dev/null
+++ b/scripts/gentest/src/main.rs
@@ -0,0 +1,364 @@
+extern crate json;
+
+extern crate selenium_rs;
+use selenium_rs::element::Element;
+use selenium_rs::webdriver::{Browser, Selector, WebDriver};
+
+use std::process::Command;
+use std::path::Path;
+use std::fs;
+use std::{thread, time};
+
+fn main() {
+ // std::env::current_exe should be <path/to/repo>/scripts/gentest/target/debug/gentest
+ // move up five times to get to repo root
+ let root_dir = std::env::current_exe().unwrap();
+ let repo_root = root_dir.parent()
+ .and_then(Path::parent)
+ .and_then(Path::parent)
+ .and_then(Path::parent)
+ .and_then(Path::parent)
+ .unwrap();
+
+ let fixtures_root = repo_root.join("test_fixtures");
+
+ let mut selenium = Command::new("java")
+ .arg("-jar")
+ .arg("selenium.jar")
+ .current_dir(&format!("{}/scripts/gentest", repo_root.display()))
+ .spawn()
+ .expect("failed to execute selenium, most likely missing java");
+
+ // Wait for a bit until selenium is up and running...
+ let ten_millis = time::Duration::from_millis(2000);
+ thread::sleep(ten_millis);
+
+ let mut driver = WebDriver::new(Browser::Chrome);
+ driver.start_session();
+
+ let mut src = String::new();
+ src.push_str("#[cfg(test)]\n");
+ src.push_str("mod generated {\n");
+
+ let fixtures = fs::read_dir(fixtures_root).unwrap();
+ for fixture in fixtures {
+ let fixture_path = fixture.unwrap().path();
+ let name = fixture_path.file_stem().unwrap().to_str().unwrap();
+
+ driver.navigate(&format!("file://{}", fixture_path.display()));
+ let root_element = driver.query_element(Selector::CSS, "#test-root").unwrap();
+ src.push_str(&format!("{}\n", generate_test(&name, root_element)));
+ }
+
+ src.push_str("}\n");
+ fs::write(repo_root.join("tests").join("generated.rs"), src);
+
+ Command::new("cargo")
+ .arg("fmt")
+ .current_dir(repo_root)
+ .status();
+
+ driver.delete_session();
+ selenium.kill();
+}
+
+fn generate_test(name: &str, root: Element) -> String {
+ let description_string = root.get_property("__stretch_description__").unwrap();
+ let description = json::parse(&description_string).unwrap();
+
+ let mut src = String::new();
+ src.push_str("#[test]\n");
+ src.push_str(&format!("fn {}() {{\n", name));
+
+ src.push_str("let layout = stretch::compute(\n");
+ src.push_str(&format!("&{}", &generate_node(&description)));
+ src.push_str(");\n\n");
+
+ src.push_str(&generate_assertions("layout".to_string(), &description));
+ src.push_str("}\n");
+ src
+}
+
+fn generate_assertions(prefix: String, node: &json::JsonValue) -> String {
+ let layout = &node["layout"];
+ let mut src = String::new();
+
+ src.push_str(&format!("assert_eq!({}.width, {:.1});\n", prefix, layout["width"].as_f32().unwrap()));
+ src.push_str(&format!("assert_eq!({}.height, {:.1});\n", prefix, layout["height"].as_f32().unwrap()));
+ src.push_str(&format!("assert_eq!({}.x, {:.1});\n", prefix, layout["x"].as_f32().unwrap()));
+ src.push_str(&format!("assert_eq!({}.y, {:.1});\n\n", prefix, layout["y"].as_f32().unwrap()));
+
+ match node["children"] {
+ json::JsonValue::Array(ref value) => {
+ for i in 0..value.len() {
+ let child = &value[i];
+ src.push_str(&generate_assertions(format!("{}.children[{}]", prefix, i), child));
+ }
+ },
+ _ => (),
+ };
+ src
+}
+
+fn generate_node(node: &json::JsonValue) -> String {
+ let mut src = String::new();
+ src.push_str("stretch::style::Node {\n");
+
+ let style = &node["style"];
+
+ match style["position"] {
+ json::JsonValue::String(ref value) => {
+ match value.as_ref() {
+ "absolute" => src.push_str("position: stretch::style::Position::Absolute,\n"),
+ _ => (),
+ };
+ },
+ _ => (),
+ };
+
+ match style["direction"] {
+ json::JsonValue::String(ref value) => {
+ match value.as_ref() {
+ "rtl" => src.push_str("direction: stretch::style::Direction::RTL,\n"),
+ "ltr" => src.push_str("direction: stretch::style::Direction::LTR,\n"),
+ _ => (),
+ };
+ },
+ _ => (),
+ };
+
+ match style["flexDirection"] {
+ json::JsonValue::String(ref value) => {
+ match value.as_ref() {
+ "row-reverse" => src.push_str("flex_direction: stretch::style::FlexDirection::RowReverse,\n"),
+ "column" => src.push_str("flex_direction: stretch::style::FlexDirection::Column,\n"),
+ "column-reverse" => src.push_str("flex_direction: stretch::style::FlexDirection::ColumnReverse,\n"),
+ _ => (),
+ };
+ },
+ _ => (),
+ };
+
+ match style["wrap"] {
+ json::JsonValue::String(ref value) => {
+ match value.as_ref() {
+ "wrap" => src.push_str("wrap: stretch::style::Wrap::Wrap,\n"),
+ "wrap-reverse" => src.push_str("wrap: stretch::style::Wrap::WrapReverse,\n"),
+ _ => (),
+ };
+ },
+ _ => (),
+ };
+
+ match style["overflow"] {
+ json::JsonValue::String(ref value) => {
+ match value.as_ref() {
+ "hidden" => src.push_str("overflow: stretch::style::Overflow::Hidden,\n"),
+ "scroll" => src.push_str("overflow: stretch::style::Overflow::Scroll,\n"),
+ _ => (),
+ };
+ },
+ _ => (),
+ };
+
+ match style["alignItems"] {
+ json::JsonValue::String(ref value) => {
+ match value.as_ref() {
+ "flex-start" => src.push_str("align_items: stretch::style::AlignItems::FlexStart,\n"),
+ "flex-end" => src.push_str("align_items: stretch::style::AlignItems::FlexEnd,\n"),
+ "center" => src.push_str("align_items: stretch::style::AlignItems::Center,\n"),
+ "baseline" => src.push_str("align_items: stretch::style::AlignItems::Baseline,\n"),
+ _ => (),
+ };
+ },
+ _ => (),
+ };
+
+ match style["alignSelf"] {
+ json::JsonValue::String(ref value) => {
+ match value.as_ref() {
+ "flex-start" => src.push_str("align_self: stretch::style::AlignSelf::FlexStart,\n"),
+ "flex-end" => src.push_str("align_self: stretch::style::AlignSelf::FlexEnd,\n"),
+ "center" => src.push_str("align_self: stretch::style::AlignSelf::Center,\n"),
+ "baseline" => src.push_str("align_self: stretch::style::AlignSelf::Baseline,\n"),
+ "stretch" => src.push_str("align_self: stretch::style::AlignSelf::Stretch,\n"),
+ _ => (),
+ };
+ },
+ _ => (),
+ };
+
+ match style["alignContent"] {
+ json::JsonValue::String(ref value) => {
+ match value.as_ref() {
+ "flex-start" => src.push_str("align_content: stretch::style::AlignContent::FlexStart,\n"),
+ "flex-end" => src.push_str("align_content: stretch::style::AlignContent::FlexEnd,\n"),
+ "center" => src.push_str("align_content: stretch::style::AlignContent::Center,\n"),
+ "space-between" => src.push_str("align_content: stretch::style::AlignContent::SpaceBetween,\n"),
+ "space-around" => src.push_str("align_content: stretch::style::AlignContent::SpaceAround,\n"),
+ _ => (),
+ };
+ },
+ _ => (),
+ };
+
+ match style["justifyContent"] {
+ json::JsonValue::String(ref value) => {
+ match value.as_ref() {
+ "flex-end" => src.push_str("justify_content: stretch::style::JustifyContent::FlexEnd,\n"),
+ "center" => src.push_str("justify_content: stretch::style::JustifyContent::Center,\n"),
+ "space-between" => src.push_str("justify_content: stretch::style::JustifyContent::SpaceBetween,\n"),
+ "space-around" => src.push_str("justify_content: stretch::style::JustifyContent::SpaceAround,\n"),
+ "space-evenly" => src.push_str("justify_content: stretch::style::JustifyContent::SpaceEvenly,\n"),
+ _ => (),
+ };
+ },
+ _ => (),
+ };
+
+ match style["flexGrow"] {
+ json::JsonValue::Number(value) => src.push_str(&format!("flex_grow: {:.1},\n", value)),
+ _ => (),
+ };
+
+ match style["flexShrink"] {
+ json::JsonValue::Number(value) => src.push_str(&format!("flex_shrink: {:.1},\n", value)),
+ _ => (),
+ };
+
+ match style["flexBasis"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("flex_basis: {},\n", generate_dimension(value))),
+ _ => (),
+ };
+
+ match style["width"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("width: {},\n", generate_dimension(value))),
+ _ => (),
+ };
+
+ match style["minWidth"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("min_width: {},\n", generate_dimension(value))),
+ _ => (),
+ };
+
+ match style["maxWidth"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("max_width: {},\n", generate_dimension(value))),
+ _ => (),
+ };
+
+ match style["height"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("height: {},\n", generate_dimension(value))),
+ _ => (),
+ };
+
+ match style["minHeight"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("min_height: {},\n", generate_dimension(value))),
+ _ => (),
+ };
+
+ match style["maxHeight"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("max_height: {},\n", generate_dimension(value))),
+ _ => (),
+ };
+
+ match style["margin"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("margin: {},\n", generate_edges(value))),
+ _ => (),
+ };
+
+ match style["padding"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("padding: {},\n", generate_edges(value))),
+ _ => (),
+ };
+
+ match style["border"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("border: {},\n", generate_edges(value))),
+ _ => (),
+ };
+
+ match style["start"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("start: {},\n", generate_dimension(value))),
+ _ => (),
+ };
+
+ match style["end"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("end: {},\n", generate_dimension(value))),
+ _ => (),
+ };
+
+ match style["top"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("top: {},\n", generate_dimension(value))),
+ _ => (),
+ };
+
+ match style["bottom"] {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("bottom: {},\n", generate_dimension(value))),
+ _ => (),
+ };
+
+ match node["children"] {
+ json::JsonValue::Array(ref value) => {
+ src.push_str("children: vec![\n");
+ value.iter().for_each(|child| {
+ src.push_str(&format!("{},\n", generate_node(child)));
+ });
+ src.push_str("],\n");
+ },
+ _ => (),
+ };
+
+ src.push_str("..Default::default()\n");
+ src.push_str("}\n");
+ src
+}
+
+fn generate_dimension(dimen: &json::object::Object) -> String {
+ let unit = dimen.get("unit").unwrap();
+ let value = || dimen.get("value").unwrap().as_f32().unwrap();
+
+ if unit == "auto" { format!("stretch::style::Dimension::Auto") }
+ else if unit == "points" { format!("stretch::style::Dimension::Points({:.1})", value()) }
+ else if unit == "percent" { format!("stretch::style::Dimension::Percent({:.1})", value()) }
+ else { panic!() }
+}
+
+fn generate_edges(dimen: &json::object::Object) -> String {
+ let mut src = String::new();
+ src.push_str("stretch::style::Edges {\n");
+
+ match dimen.get("start") {
+ Some(value) => match value {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("start: {},\n", generate_dimension(value))),
+ _ => (),
+ },
+ None => (),
+ };
+
+ match dimen.get("end") {
+ Some(value) => match value {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("end: {},\n", generate_dimension(value))),
+ _ => (),
+ },
+ None => (),
+ };
+
+ match dimen.get("top") {
+ Some(value) => match value {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("top: {},\n", generate_dimension(value))),
+ _ => (),
+ },
+ None => (),
+ };
+
+ match dimen.get("bottom") {
+ Some(value) => match value {
+ json::JsonValue::Object(ref value) => src.push_str(&format!("bottom: {},\n", generate_dimension(value))),
+ _ => (),
+ },
+ None => (),
+ };
+
+ src.push_str("..Default::default()\n");
+ src.push_str("}\n");
+ src
+}
diff --git a/scripts/gentest/test_base_style.css b/scripts/gentest/test_base_style.css
new file mode 100644
index 000000000..16f4a1375
--- /dev/null
+++ b/scripts/gentest/test_base_style.css
@@ -0,0 +1,17 @@
+body {
+ padding: 0;
+ margin: 0;
+}
+
+div, span, img {
+ box-sizing: border-box;
+ position: relative;
+ border: 0 solid black;
+ margin: 0;
+ padding: 0;
+ display: flex;
+}
+
+body > * {
+ position: absolute;
+}
\ No newline at end of file
diff --git a/scripts/gentest/test_helper.js b/scripts/gentest/test_helper.js
new file mode 100644
index 000000000..270c9e3f7
--- /dev/null
+++ b/scripts/gentest/test_helper.js
@@ -0,0 +1,129 @@
+function define_element_prop(property, getter) {
+ if (!(property in Element.prototype)) {
+ Object.defineProperty(Element.prototype, property, {
+ get: function() {
+ return getter(this);
+ }
+ });
+ }
+}
+
+define_element_prop("__stretch_description__", (e) => {
+ return JSON.stringify(describeElement(e));
+});
+
+function parseDimension(input) {
+ if (input.endsWith("px")) {
+ return {
+ unit: 'points',
+ value: Number(input.replace('px',''))
+ };
+ } else if (input.endsWith("%")) {
+ return {
+ unit: 'percent',
+ value: Number(input.replace('%','')) / 100
+ };
+ } else {
+ return input == "auto" ? {unit: "auto"} : undefined;
+ }
+}
+
+function parseNumber(input) {
+ if (input === "" || isNaN(input)) {
+ return undefined;
+ } else {
+ return Number(input);
+ }
+}
+
+function parseEnum(input) {
+ if (input) {
+ return input;
+ } else {
+ return undefined;
+ }
+}
+
+function parseEdges(edges) {
+ var start = parseDimension(edges.start);
+ var end = parseDimension(edges.end);
+ var top = parseDimension(edges.top);
+ var bottom = parseDimension(edges.bottom);
+
+ if (start === undefined && end === undefined && top === undefined && bottom === undefined) {
+ return undefined;
+ }
+
+ return {
+ start: start,
+ end: end,
+ top: top,
+ bottom: bottom
+ };
+}
+
+function describeElement(e) {
+ return {
+ style: {
+ position: parseEnum(e.style.position),
+ direction: parseEnum(e.style.direction),
+ flexDirection: parseEnum(e.style.flexDirection),
+
+ wrap: parseEnum(e.style.wrap),
+ overflow: parseEnum(e.style.overflow),
+
+ alignItems: parseEnum(e.style.alignItems),
+ alignSelf: parseEnum(e.style.alignSelf),
+ alignContent: parseEnum(e.style.alignContent),
+
+ justifyContent: parseEnum(e.style.justifyContent),
+
+ flexGrow: parseNumber(e.style.flexGrow),
+ flexShrink: parseNumber(e.style.flexShrink),
+ flexBasis: parseDimension(e.style.flexBasis),
+
+ width: parseDimension(e.style.width),
+ minWidth: parseDimension(e.style.minWidth),
+ maxWidth: parseDimension(e.style.maxWidth),
+
+ height: parseDimension(e.style.height),
+ minHeight: parseDimension(e.style.minHeight),
+ maxHeight: parseDimension(e.style.maxHeight),
+
+ margin: parseEdges({
+ start: e.style.marginLeft,
+ end: e.style.marginRight,
+ top: e.style.marginTop,
+ bottom: e.style.marginBottom,
+ }),
+
+ padding: parseEdges({
+ start: e.style.paddingLeft,
+ end: e.style.paddingRight,
+ top: e.style.paddingTop,
+ bottom: e.style.paddingBottom,
+ }),
+
+ border: parseEdges({
+ start: e.style.borderLeft,
+ end: e.style.borderRight,
+ top: e.style.borderTop,
+ bottom: e.style.borderBottom,
+ }),
+
+ start: parseDimension(e.style.left),
+ end: parseDimension(e.style.right),
+ top: parseDimension(e.style.top),
+ bottom: parseDimension(e.style.bottom),
+ },
+
+ layout: {
+ width: e.offsetWidth,
+ height: e.offsetHeight,
+ x: e.offsetLeft,
+ y: e.offsetTop,
+ },
+
+ children: Array.from(e.children).map(c => describeElement(c)),
+ }
+}
diff --git a/src/algo.rs b/src/algo.rs
index 4fc059131..2e8b9f0ba 100644
--- a/src/algo.rs
+++ b/src/algo.rs
@@ -1,781 +1,913 @@
use std::f32;
-use style;
use layout;
+use style;
#[derive(Debug, Copy, Clone)]
struct FlexSize {
- main: f32,
- cross: f32,
+ main: f32,
+ cross: f32,
}
#[derive(Debug, Copy, Clone)]
struct SizeConstraint {
- min: f32,
- max: f32,
+ min: f32,
+ max: f32,
}
impl SizeConstraint {
- fn exactly(size: f32) -> SizeConstraint {
- SizeConstraint {
- min: size,
- max: size,
+ fn exactly(size: f32) -> SizeConstraint {
+ SizeConstraint {
+ min: size,
+ max: size,
+ }
}
- }
- fn undefined() -> SizeConstraint {
- SizeConstraint {
- min: f32::NEG_INFINITY,
- max: f32::INFINITY,
+ fn undefined() -> SizeConstraint {
+ SizeConstraint {
+ min: f32::NEG_INFINITY,
+ max: f32::INFINITY,
+ }
}
- }
- fn at_least(size: f32) -> SizeConstraint {
- SizeConstraint {
- min: size,
- max: f32::NEG_INFINITY,
+ fn at_least(size: f32) -> SizeConstraint {
+ SizeConstraint {
+ min: size,
+ max: f32::NEG_INFINITY,
+ }
}
- }
- fn at_most(size: f32) -> SizeConstraint {
- SizeConstraint {
- min: f32::NEG_INFINITY,
- max: size,
+ fn at_most(size: f32) -> SizeConstraint {
+ SizeConstraint {
+ min: f32::NEG_INFINITY,
+ max: size,
+ }
}
- }
- fn between(min: f32, max: f32) -> SizeConstraint {
- SizeConstraint {
- min: min,
- max: max,
+ fn between(min: f32, max: f32) -> SizeConstraint {
+ SizeConstraint { min: min, max: max }
}
- }
}
struct FlexItem<'a> {
- node: &'a style::Node,
-
- // temporary values for main size determination
- flex_basis: f32,
- hypothetical_inner_main_size: f32,
- hypothetical_outer_main_size: f32,
- target_main_size: f32,
- violation: f32,
- frozen: bool,
-
- // temporary values for cross size determination
- hypothetical_inner_cross_size: f32,
- hypothetical_outer_cross_size: f32,
- target_cross_size: f32,
-
- main_margin_start: f32,
- main_margin_end: f32,
- cross_margin_start: f32,
- cross_margin_end: f32,
-
- // temporary values for holding offset in the main / cross direction.
- // offset is the relative position from the item's natural flow position based on
- // relative position values, alignment, and justification. Does not unclude margin/padding/border.
- offset_main: f32,
- offset_cross: f32,
+ node: &'a style::Node,
+
+ // temporary values for main size determination
+ flex_basis: f32,
+ hypothetical_inner_main_size: f32,
+ hypothetical_outer_main_size: f32,
+ target_main_size: f32,
+ violation: f32,
+ frozen: bool,
+
+ // temporary values for cross size determination
+ hypothetical_inner_cross_size: f32,
+ hypothetical_outer_cross_size: f32,
+ target_cross_size: f32,
+
+ main_margin_start: f32,
+ main_margin_end: f32,
+ cross_margin_start: f32,
+ cross_margin_end: f32,
+
+ // temporary values for holding offset in the main / cross direction.
+ // offset is the relative position from the item's natural flow position based on
+ // relative position values, alignment, and justification. Does not unclude margin/padding/border.
+ offset_main: f32,
+ offset_cross: f32,
}
struct ComputeResult {
- size: FlexSize,
- children: Vec<layout::Node>,
+ size: FlexSize,
+ children: Vec<layout::Node>,
}
struct FlexLine<'a> {
- items: Vec<FlexItem<'a>>,
- cross_size: f32,
- offset_cross: f32,
+ items: Vec<FlexItem<'a>>,
+ cross_size: f32,
+ offset_cross: f32,
}
pub fn compute(root: &style::Node) -> layout::Node {
- let result = compute_internal(root, SizeConstraint::undefined(), SizeConstraint::undefined());
-
- layout::Node {
- width: if root.flex_direction.is_row() { result.size.main } else { result.size.cross },
- height: if root.flex_direction.is_column() { result.size.main } else { result.size.cross },
- x: 0.0,
- y: 0.0,
- children: result.children,
- }
+ let result = compute_internal(
+ root,
+ SizeConstraint::undefined(),
+ SizeConstraint::undefined(),
+ );
+
+ layout::Node {
+ width: if root.flex_direction.is_row() {
+ result.size.main
+ } else {
+ result.size.cross
+ },
+ height: if root.flex_direction.is_column() {
+ result.size.main
+ } else {
+ result.size.cross
+ },
+ x: 0.0,
+ y: 0.0,
+ children: result.children,
+ }
}
-fn compute_internal(node: &style::Node, main: SizeConstraint, cross: SizeConstraint) -> ComputeResult {
- // 9.2. Line Length Determination
+fn compute_internal(
+ node: &style::Node,
+ main: SizeConstraint,
+ cross: SizeConstraint,
+) -> ComputeResult {
+ // 9.2. Line Length Determination
+
+ // 1. Generate anonymous flex items as described in §4 Flex Items.
+
+ let mut flex_items: Vec<FlexItem> = node
+ .children
+ .iter()
+ .map(|child| FlexItem {
+ node: child,
+
+ flex_basis: 0.0,
+ hypothetical_inner_main_size: 0.0,
+ hypothetical_outer_main_size: 0.0,
+ target_main_size: 0.0,
+ violation: 0.0,
+ frozen: false,
+
+ hypothetical_inner_cross_size: 0.0,
+ hypothetical_outer_cross_size: 0.0,
+ target_cross_size: 0.0,
+
+ main_margin_start: 0.0,
+ main_margin_end: 0.0,
+ cross_margin_start: 0.0,
+ cross_margin_end: 0.0,
+
+ offset_main: 0.0,
+ offset_cross: 0.0,
+ }).collect();
+
+ // 2. Determine the available main and cross space for the flex items.
+ // For each dimension, if that dimension of the flex container’s content box
+ // is a definite size, use that; if that dimension of the flex container is
+ // being sized under a min or max-content constraint, the available space in
+ // that dimension is that constraint; otherwise, subtract the flex container’s
+ // margin, border, and padding from the space available to the flex container
+ // in that dimension and use that value. This might result in an infinite value.
+
+ let avaliable_main = node.main_size().resolve(main.max, {
+ main.max
+ - node.main_margin_start().resolve(main.max, 0.0)
+ - node.main_margin_end().resolve(main.max, 0.0)
+ }) - node.main_padding_start().resolve(main.max, 0.0)
+ - node.main_padding_end().resolve(main.max, 0.0)
+ - node.main_border_start().resolve(main.max, 0.0)
+ - node.main_border_end().resolve(main.max, 0.0);
+
+ let avaliable_cross = node.cross_size().resolve(cross.max, {
+ cross.max
+ - node.cross_margin_start().resolve(cross.max, 0.0)
+ - node.cross_margin_end().resolve(cross.max, 0.0)
+ }) - node.cross_padding_start().resolve(cross.max, 0.0)
+ - node.cross_padding_end().resolve(cross.max, 0.0)
+ - node.cross_border_start().resolve(cross.max, 0.0)
+ - node.cross_border_end().resolve(cross.max, 0.0);
+
+ // 3. Determine the flex base size and hypothetical main size of each item:
+ for child in &mut flex_items {
+ // A. If the item has a definite used flex basis, that’s the flex base size.
+
+ let flex_basis = child.node.flex_basis.resolve(avaliable_main, f32::INFINITY);
+ if flex_basis.is_finite() {
+ child.flex_basis = flex_basis;
+ continue;
+ }
- // 1. Generate anonymous flex items as described in §4 Flex Items.
+ // B. If the flex item has an intrinsic aspect ratio,
+ // a used flex basis of content, and a definite cross size,
+ // then the flex base size is calculated from its inner
+ // cross size and the flex item’s intrinsic aspect ratio.
- let mut flex_items: Vec<FlexItem> = node.children.iter().map(|child| {
- FlexItem {
- node: child,
+ if let Some(ratio) = child.node.aspect_ratio {
+ let cross_size = node.cross_size().resolve(avaliable_cross, f32::INFINITY);
- flex_basis: 0.0,
- hypothetical_inner_main_size: 0.0,
- hypothetical_outer_main_size: 0.0,
- target_main_size: 0.0,
- violation: 0.0,
- frozen: false,
+ if cross_size.is_finite() && child.node.flex_basis == style::Dimension::Auto {
+ child.flex_basis = ratio * cross_size;
+ continue;
+ }
+ }
- hypothetical_inner_cross_size: 0.0,
- hypothetical_outer_cross_size: 0.0,
- target_cross_size: 0.0,
+ // C. If the used flex basis is content or depends on its available space,
+ // and the flex container is being sized under a min-content or max-content
+ // constraint (e.g. when performing automatic table layout [CSS21]),
+ // size the item under that constraint. The flex base size is the item’s
+ // resulting main size.
- main_margin_start: 0.0,
- main_margin_end: 0.0,
- cross_margin_start: 0.0,
- cross_margin_end: 0.0,
+ if main.min.is_finite() || main.max.is_finite() {
+ let size = compute_internal(child.node, main, cross).size;
+ child.flex_basis = size.main;
+ continue;
+ }
- offset_main: 0.0,
- offset_cross: 0.0,
- }
- }).collect();
-
- // 2. Determine the available main and cross space for the flex items.
- // For each dimension, if that dimension of the flex container’s content box
- // is a definite size, use that; if that dimension of the flex container is
- // being sized under a min or max-content constraint, the available space in
- // that dimension is that constraint; otherwise, subtract the flex container’s
- // margin, border, and padding from the space available to the flex container
- // in that dimension and use that value. This might result in an infinite value.
-
- let avaliable_main = node.main_size().resolve(main.max, {
- main.max
- - node.main_margin_start().resolve(main.max, 0.0)
- - node.main_margin_end().resolve(main.max, 0.0)
- })
- - node.main_padding_start().resolve(main.max, 0.0)
- - node.main_padding_end().resolve(main.max, 0.0)
- - node.main_border_start().resolve(main.max, 0.0)
- - node.main_border_end().resolve(main.max, 0.0);
-
- let avaliable_cross = node.cross_size().resolve(cross.max, {
- cross.max
- - node.cross_margin_start().resolve(cross.max, 0.0)
- - node.cross_margin_end().resolve(cross.max, 0.0)
- })
- - node.cross_padding_start().resolve(cross.max, 0.0)
- - node.cross_padding_end().resolve(cross.max, 0.0)
- - node.cross_border_start().resolve(cross.max, 0.0)
- - node.cross_border_end().resolve(cross.max, 0.0);
-
- // 3. Determine the flex base size and hypothetical main size of each item:
- for child in &mut flex_items {
- // A. If the item has a definite used flex basis, that’s the flex base size.
-
- let flex_basis = child.node.flex_basis.resolve(avaliable_main, f32::INFINITY);
- if flex_basis.is_finite() {
- child.flex_basis = flex_basis;
- continue;
+ // D. Otherwise, if the used flex basis is content or depends on its
+ // available space, the available main size is infinite, and the flex item’s
+ // inline axis is parallel to the main axis, lay the item out using the rules
+ // for a box in an orthogonal flow [CSS3-WRITING-MODES]. The flex base size
+ // is the item’s max-content main size.
+
+ if avaliable_main.is_infinite() {
+ let size = compute_internal(
+ child.node,
+ SizeConstraint::undefined(),
+ SizeConstraint::undefined(),
+ ).size;
+ child.flex_basis = size.main;
+ continue;
+ }
+
+ // E. Otherwise, size the item into the available space using its used flex basis
+ // in place of its main size, treating a value of content as max-content.
+ // If a cross size is needed to determine the main size (e.g. when the
+ // flex item’s main size is in its block axis) and the flex item’s cross size
+ // is auto and not definite, in this calculation use fit-content as the
+ // flex item’s cross size. The flex base size is the item’s resulting main size.
+
+ let size = compute_internal(
+ child.node,
+ SizeConstraint::at_most(avaliable_main),
+ SizeConstraint::at_most(avaliable_cross),
+ ).size;
+ child.flex_basis = size.main;
}
- // B. If the flex item has an intrinsic aspect ratio,
- // a used flex basis of content, and a definite cross size,
- // then the flex base size is calculated from its inner
- // cross size and the flex item’s intrinsic aspect ratio.
+ // The hypothetical main size is the item’s flex base size clamped according to its
+ // used min and max main sizes (and flooring the content box size at zero).
- if let Some(ratio) = child.node.aspect_ratio {
- let cross_size = node.cross_size().resolve(avaliable_cross, f32::INFINITY);
+ for child in &mut flex_items {
+ child.hypothetical_inner_main_size = child
+ .flex_basis
+ .max(child.node.min_main_size().resolve(avaliable_main, f32::MIN))
+ .min(child.node.max_main_size().resolve(avaliable_main, f32::MAX))
+ .max(0.0);
- if cross_size.is_finite() && child.node.flex_basis == style::Dimension::Auto {
- child.flex_basis = ratio * cross_size;
- continue;
- }
+ child.hypothetical_outer_main_size = child.hypothetical_inner_main_size
+ + child.node.main_margin_start().resolve(avaliable_main, 0.0)
+ + child.node.main_margin_end().resolve(avaliable_main, 0.0);
}
- // C. If the used flex basis is content or depends on its available space,
- // and the flex container is being sized under a min-content or max-content
- // constraint (e.g. when performing automatic table layout [CSS21]),
- // size the item under that constraint. The flex base size is the item’s
- // resulting main size.
+ // 9.3. Main Size Determination
+
+ // 5. Collect flex items into flex lines:
+ // - If the flex container is single-line, collect all the flex items into
+ // a single flex line.
+ // - Otherwise, starting from the first uncollected item, collect consecutive
+ // items one by one until the first time that the next collected item would
+ // not fit into the flex container’s inner main size (or until a forced break
+ // is encountered, see §10 Fragmenting Flex Layout). If the very first
+ // uncollected item wouldn’t fit, collect just it into the line.
+ //
+ // For this step, the size of a flex item is its outer hypothetical main size. (Note: This can be negative.)
+ // Repeat until all flex items have been collected into flex lines
+ //
+ // Note that the "collect as many" line will collect zero-sized flex items onto
+ // the end of the previous line even if the last non-zero item exactly "filled up" the line.
+
+ let mut flex_lines = {
+ let mut lines: Vec<FlexLine> = vec![];
+ let mut line_length = 0.0;
+
+ if node.wrap == style::Wrap::NoWrap {
+ lines.push(FlexLine {
+ items: flex_items,
+ cross_size: 0.0,
+ offset_cross: 0.0,
+ });
+ } else {
+ let mut line = FlexLine {
+ items: vec![],
+ cross_size: 0.0,
+ offset_cross: 0.0,
+ };
+
+ for child in flex_items {
+ line_length += child.hypothetical_outer_main_size;
+
+ if line_length > avaliable_main && line.items.len() > 0 {
+ line_length = child.hypothetical_outer_main_size;
+ lines.push(line);
+ line = FlexLine {
+ items: vec![],
+ cross_size: 0.0,
+ offset_cross: 0.0,
+ };
+ }
+
+ line.items.push(child);
+ }
+
+ lines.push(line);
+ }
- if main.min.is_finite() || main.max.is_finite() {
- let size = compute_internal(child.node, main, cross).size;
- child.flex_basis = size.main;
- continue;
- }
+ lines
+ };
- // D. Otherwise, if the used flex basis is content or depends on its
- // available space, the available main size is infinite, and the flex item’s
- // inline axis is parallel to the main axis, lay the item out using the rules
- // for a box in an orthogonal flow [CSS3-WRITING-MODES]. The flex base size
- // is the item’s max-content main size.
+ // 6. Resolve the flexible lengths of all the flex items to find their used main size.
+ // See §9.7 Resolving Flexible Lengths.
+ //
+ // 9.7. Resolving Flexible Lengths
- if avaliable_main.is_infinite() {
- let size = compute_internal(child.node, SizeConstraint::undefined(), SizeConstraint::undefined()).size;
- child.flex_basis = size.main;
- continue;
+ 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.
+
+ let used_flex_factor: f32 = line
+ .items
+ .iter()
+ .map(|child| child.hypothetical_outer_main_size)
+ .sum();
+ let growing = used_flex_factor < avaliable_main;
+ let shrinking = !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
+
+ for child in line.items.iter_mut() {
+ child.target_main_size = child.hypothetical_inner_main_size;
+
+ if (child.node.flex_grow == 0.0 && child.node.flex_shrink == 0.0)
+ || (growing && child.flex_basis > child.hypothetical_inner_main_size)
+ || (shrinking && child.flex_basis < child.hypothetical_inner_main_size)
+ {
+ 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.
+
+ let used_space: f32 = line
+ .items
+ .iter()
+ .map(|child| {
+ child.node.main_margin_start().resolve(avaliable_main, 0.0)
+ + child.node.main_margin_end().resolve(avaliable_main, 0.0)
+ + if child.frozen {
+ child.target_main_size
+ } else {
+ child.flex_basis
+ }
+ }).sum();
+
+ let initial_free_space = avaliable_main - used_space;
+
+ // 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.
+
+ let mut frozen: Vec<&mut FlexItem> = vec![];
+ let mut unfrozen: Vec<&mut FlexItem> = vec![];
+
+ for child in line.items.iter_mut() {
+ if child.frozen {
+ frozen.push(child);
+ } else {
+ unfrozen.push(child);
+ }
+ }
+
+ if unfrozen.len() == 0 {
+ 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.
+
+ let used_space: f32 = Iterator::chain(frozen.iter(), unfrozen.iter())
+ .map(|child| {
+ child.node.main_margin_start().resolve(avaliable_main, 0.0)
+ + child.node.main_margin_end().resolve(avaliable_main, 0.0)
+ + if child.frozen {
+ child.target_main_size
+ } else {
+ child.flex_basis
+ }
+ }).sum();
+
+ let sum_flex_grow: f32 = unfrozen.iter().map(|item| item.node.flex_grow).sum();
+ let sum_flex_shrink: f32 = unfrozen.iter().map(|item| item.node.flex_shrink).sum();
+
+ let free_space = if growing && sum_flex_grow < 1.0 {
+ (initial_free_space * sum_flex_grow).min(avaliable_main - used_space)
+ } else if shrinking && sum_flex_shrink < 1.0 {
+ (initial_free_space * sum_flex_shrink).max(avaliable_main - used_space)
+ } else {
+ avaliable_main - used_space
+ };
+
+ // 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
+
+ if free_space > 0.0 && growing {
+ for child in &mut unfrozen {
+ child.target_main_size =
+ child.flex_basis + free_space * (child.node.flex_grow / sum_flex_grow);
+ }
+ } else if free_space < 0.0 && shrinking {
+ for child in &mut unfrozen {
+ child.target_main_size =
+ child.flex_basis + free_space * (child.node.flex_shrink / sum_flex_shrink);
+ }
+ }
+
+ // 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 mut total_violation = 0.0;
+ for child in &mut unfrozen {
+ let max = child.node.max_main_size().resolve(avaliable_main, f32::MAX);
+ let min = child
+ .node
+ .min_main_size()
+ .resolve(avaliable_main, f32::MIN)
+ .max(0.0);
+
+ let clamped = if child.target_main_size > max {
+ max
+ } else if child.target_main_size < min {
+ min
+ } else {
+ child.target_main_size
+ };
+
+ child.violation = clamped - child.target_main_size;
+ total_violation += child.violation;
+ child.target_main_size = clamped;
+ }
+
+ // 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.
+
+ if total_violation > 0.0 {
+ for child in &mut unfrozen {
+ child.frozen = child.violation < 0.0;
+ }
+ } else if total_violation < 0.0 {
+ for child in &mut unfrozen {
+ child.frozen = child.violation > 0.0;
+ }
+ } else {
+ for child in &mut unfrozen {
+ child.frozen = true;
+ }
+ }
+
+ // f. Return to the start of this loop.
+ }
}
- // E. Otherwise, size the item into the available space using its used flex basis
- // in place of its main size, treating a value of content as max-content.
- // If a cross size is needed to determine the main size (e.g. when the
- // flex item’s main size is in its block axis) and the flex item’s cross size
- // is auto and not definite, in this calculation use fit-content as the
- // flex item’s cross size. The flex base size is the item’s resulting main size.
-
- let size = compute_internal(child.node, SizeConstraint::at_most(avaliable_main), SizeConstraint::at_most(avaliable_cross)).size;
- child.flex_basis = size.main;
- }
-
- // The hypothetical main size is the item’s flex base size clamped according to its
- // used min and max main sizes (and flooring the content box size at zero).
-
- for child in &mut flex_items {
- child.hypothetical_inner_main_size = child.flex_basis
- .max(child.node.min_main_size().resolve(avaliable_main, f32::MIN))
- .min(child.node.max_main_size().resolve(avaliable_main, f32::MAX))
- .max(0.0);
-
- child.hypothetical_outer_main_size = child.hypothetical_inner_main_size
- + child.node.main_margin_start().resolve(avaliable_main, 0.0)
- + child.node.main_margin_end().resolve(avaliable_main, 0.0);
- }
-
- // 9.3. Main Size Determination
-
- // 5. Collect flex items into flex lines:
- // - If the flex container is single-line, collect all the flex items into
- // a single flex line.
- // - Otherwise, starting from the first uncollected item, collect consecutive
- // items one by one until the first time that the next collected item would
- // not fit into the flex container’s inner main size (or until a forced break
- // is encountered, see §10 Fragmenting Flex Layout). If the very first
- // uncollected item wouldn’t fit, collect just it into the line.
- //
- // For this step, the size of a flex item is its outer hypothetical main size. (Note: This can be negative.)
- // Repeat until all flex items have been collected into flex lines
- //
- // Note that the "collect as many" line will collect zero-sized flex items onto
- // the end of the previous line even if the last non-zero item exactly "filled up" the line.
-
- let mut flex_lines = {
- let mut lines: Vec<FlexLine> = vec![];
- let mut line_length = 0.0;
-
- if node.wrap == style::Wrap::NoWrap {
- lines.push(FlexLine { items: flex_items, cross_size: 0.0, offset_cross: 0.0 });
+ // Not part of the spec from what i can see but seems correct
+ let container_main_size = if avaliable_main.is_finite() {
+ avaliable_main
} else {
- let mut line = FlexLine { items: vec![], cross_size: 0.0, offset_cross: 0.0 };
+ let mut longest_line = f32::MIN;
- for child in flex_items {
- line_length += child.hypothetical_outer_main_size;
+ for line in &flex_lines {
+ let length: f32 = line.items.iter().map(|item| item.target_main_size).sum();
- if line_length > avaliable_main && line.items.len() > 0 {
- line_length = child.hypothetical_outer_main_size;
- lines.push(line);
- line = FlexLine { items: vec![], cross_size: 0.0, offset_cross: 0.0 };
+ longest_line = if length > longest_line {
+ length
+ } else {
+ longest_line
+ };
}
- line.items.push(child);
- }
-
- lines.push(line);
- }
+ longest_line
+ }.min(main.max)
+ .max(main.min)
+ .max(0.0);
- lines
- };
-
- // 6. Resolve the flexible lengths of all the flex items to find their used main size.
- // See §9.7 Resolving Flexible Lengths.
- //
- // 9.7. Resolving Flexible Lengths
-
- 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.
-
- let used_flex_factor: f32 = line.items.iter().map(|child| child.hypothetical_outer_main_size).sum();
- let growing = used_flex_factor < avaliable_main;
- let shrinking = !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
-
- for child in line.items.iter_mut() {
- child.target_main_size = child.hypothetical_inner_main_size;
-
- if (child.node.flex_grow == 0.0 && child.node.flex_shrink == 0.0) ||
- (growing && child.flex_basis > child.hypothetical_inner_main_size) ||
- (shrinking && child.flex_basis < child.hypothetical_inner_main_size) {
- child.frozen = true;
- }
- }
+ // 9.4. Cross Size Determination
- // 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.
+ // 7. Determine the hypothetical cross size of each item by performing layout with the
+ // used main size and the available space, treating auto as fit-content.
- let used_space: f32 = line.items.iter().map(|child| {
- child.node.main_margin_start().resolve(avaliable_main, 0.0) +
- child.node.main_margin_end().resolve(avaliable_main, 0.0) +
- if child.frozen {
- child.target_main_size
- } else {
- child.flex_basis
- }
- }).sum();
+ for line in &mut flex_lines {
+ for child in &mut line.items {
+ let size = compute_internal(
+ child.node,
+ SizeConstraint::exactly(child.target_main_size),
+ SizeConstraint::between(cross.min, avaliable_cross),
+ ).size;
+
+ child.hypothetical_inner_cross_size = size
+ .cross
+ .max(
+ child
+ .node
+ .min_cross_size()
+ .resolve(avaliable_cross, f32::MIN),
+ ).min(
+ child
+ .node
+ .max_cross_size()
+ .resolve(avaliable_cross, f32::MAX),
+ ).max(0.0);
+
+ child.hypothetical_outer_cross_size = child.hypothetical_inner_cross_size
+ + node.cross_margin_start().resolve(avaliable_cross, 0.0)
+ + node.cross_margin_end().resolve(avaliable_cross, 0.0);
+ }
+ }
- let initial_free_space = avaliable_main - used_space;
+ // 8. Calculate the cross size of each flex line.
+ // If the flex container is single-line and has a definite cross size, the cross size
+ // of the flex line is the flex container’s inner cross size. Otherwise, for each flex line:
+ //
+ // 1. Collect all the flex items whose inline-axis is parallel to the main-axis, whose
+ // align-self is baseline, and whose cross-axis margins are both non-auto. Find the
+ // largest of the distances between each item’s baseline and its hypothetical outer
+ // cross-start edge, and the largest of the distances between each item’s baseline
+ // and its hypothetical outer cross-end edge, and sum these two values.
+ // 2. Among all the items not collected by the previous step, find the largest
+ // outer hypothetical cross size.
+ // 3. The used cross-size of the flex line is the largest of the numbers found in the
+ // previous two steps and zero.
+ //
+ // If the flex container is single-line, then clamp the line’s cross-size to be within
+ // the container’s computed min and max cross sizes. Note that if CSS 2.1’s definition
+ // of min/max-width/height applied more generally, this behavior would fall out automatically.
+
+ if flex_lines.len() == 1 && avaliable_cross.is_finite() {
+ flex_lines[0].cross_size = avaliable_cross.min(cross.max).max(cross.min);
+ } else {
+ for line in &mut flex_lines {
+ // TODO handle baseline (1)
+
+ line.cross_size = line
+ .items
+ .iter()
+ .map(|child| child.hypothetical_outer_cross_size)
+ .fold(0.0, |acc, x| acc.max(x));
+ }
+ }
- // 4. Loop
+ // 9. Handle 'align-content: stretch'. If the flex container has a definite cross size,
+ // align-content is stretch, and the sum of the flex lines' cross sizes is less than
+ // the flex container’s inner cross size, increase the cross size of each flex line
+ // by equal amounts such that the sum of their cross sizes exactly equals the
+ // flex container’s inner cross size.
- loop {
- // a. Check for flexible items. If all the flex items on the line are frozen,
- // free space has been distributed; exit this loop.
+ if node.align_content == style::AlignContent::Stretch && avaliable_cross.is_finite() {
+ let total_cross: f32 = flex_lines.iter().map(|line| line.cross_size).sum();
- let mut frozen: Vec<&mut FlexItem> = vec![];
- let mut unfrozen: Vec<&mut FlexItem> = vec![];
+ if total_cross < avaliable_cross {
+ let remaining = avaliable_cross - total_cross;
+ let addition = remaining / flex_lines.len() as f32;
- for child in line.items.iter_mut() {
- if child.frozen {
- frozen.push(child);
- } else {
- unfrozen.push(child);
- }
- }
-
- if unfrozen.len() == 0 {
- 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.
-
- let used_space: f32 = Iterator::chain(frozen.iter(), unfrozen.iter()).map(|child| {
- child.node.main_margin_start().resolve(avaliable_main, 0.0) +
- child.node.main_margin_end().resolve(avaliable_main, 0.0) +
- if child.frozen {
- child.target_main_size
- } else {
- child.flex_basis
- }
- }).sum();
-
- let sum_flex_grow: f32 = unfrozen.iter().map(|item| { item.node.flex_grow }).sum();
- let sum_flex_shrink: f32 = unfrozen.iter().map(|item| { item.node.flex_shrink }).sum();
-
- let free_space = if growing && sum_flex_grow < 1.0 {
- (initial_free_space * sum_flex_grow).min(avaliable_main - used_space)
- } else if shrinking && sum_flex_shrink < 1.0 {
- (initial_free_space * sum_flex_shrink).max(avaliable_main - used_space)
- } else {
- avaliable_main - used_space
- };
-
- // 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
-
- if free_space > 0.0 && growing {
- for child in &mut unfrozen {
- child.target_main_size = child.flex_basis + free_space * (child.node.flex_grow / sum_flex_grow);
+ for line in &mut flex_lines {
+ line.cross_size += addition;
+ }
}
- } else if free_space < 0.0 && shrinking {
- for child in &mut unfrozen {
- child.target_main_size = child.flex_basis + free_space * (child.node.flex_shrink / sum_flex_shrink);
- }
- }
-
- // 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 mut total_violation = 0.0;
- for child in &mut unfrozen {
- let max = child.node.max_main_size().resolve(avaliable_main, f32::MAX);
- let min = child.node.min_main_size().resolve(avaliable_main, f32::MIN).max(0.0);
-
- let clamped = if child.target_main_size > max {
- max
- } else if child.target_main_size < min {
- min
- } else {
- child.target_main_size
- };
+ }
- child.violation = clamped - child.target_main_size;
- total_violation += child.violation;
- child.target_main_size = clamped;
- }
-
- // 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.
-
- if total_violation > 0.0 {
- for child in &mut unfrozen {
- child.frozen = child.violation < 0.0;
- }
- } else if total_violation < 0.0 {
- for child in &mut unfrozen {
- child.frozen = child.violation > 0.0;
- }
- } else {
- for child in &mut unfrozen {
- child.frozen = true;
- }
- }
+ // 10. Collapse visibility:collapse items. If any flex items have visibility: collapse,
+ // note the cross size of the line they’re in as the item’s strut size, and restart
+ // layout from the beginning.
+ //
+ // In this second layout round, when collecting items into lines, treat the collapsed
+ // items as having zero main size. For the rest of the algorithm following that step,
+ // ignore the collapsed items entirely (as if they were display:none) except that after
+ // calculating the cross size of the lines, if any line’s cross size is less than the
+ // largest strut size among all the collapsed items in the line, set its cross size to
+ // that strut size.
+ //
+ // Skip this step in the second layout round.
+
+ // TODO implement once (if ever) we support visibility:collapse
+
+ // 11. Determine the used cross size of each flex item. If a flex item has align-self: stretch,
+ // its computed cross size property is auto, and neither of its cross-axis margins are auto,
+ // the used outer cross size is the used cross size of its flex line, clamped according to
+ // the item’s used min and max cross sizes. Otherwise, the used cross size is the item’s
+ // hypothetical cross size.
+ //
+ // If the flex item has align-self: stretch, redo layout for its contents, treating this
+ // used size as its definite cross size so that percentage-sized children can be resolved.
+ //
+ // Note that this step does not affect the main size of the flex item, even if it has an
+ // intrinsic aspect ratio.
- // f. Return to the start of this loop.
- }
- }
-
- // Not part of the spec from what i can see but seems correct
- let container_main_size = if avaliable_main.is_finite() {
- avaliable_main
- } else {
- let mut longest_line = f32::MIN;
-
- for line in &flex_lines {
- let length: f32 = line.items.iter().map(|item| {
- item.target_main_size
- }).sum();
-
- longest_line = if length > longest_line {
- length
- } else {
- longest_line
- };
+ for line in &mut flex_lines {
+ for child in &mut line.items {
+ let is_stretch = child.node.align_self(node) == style::AlignSelf::Stretch;
+
+ if is_stretch
+ && child.node.cross_margin_start() != style::Dimension::Auto
+ && child.node.cross_margin_end() != style::Dimension::Auto
+ && child.node.cross_size() == style::Dimension::Auto
+ {
+ child.target_cross_size = line
+ .cross_size
+ .min(
+ child
+ .node
+ .max_cross_size()
+ .resolve(avaliable_cross, f32::MAX),
+ ).max(
+ child
+ .node
+ .min_cross_size()
+ .resolve(avaliable_cross, f32::MIN),
+ );
+ } else {
+ child.target_cross_size = child.hypothetical_inner_cross_size;
+ }
+
+ if is_stretch {
+ // TODO use result somehow
+ compute_internal(
+ child.node,
+ SizeConstraint::exactly(child.target_main_size),
+ SizeConstraint::exactly(child.target_cross_size),
+ );
+ }
+ }
}
- longest_line
- }.min(main.max).max(main.min).max(0.0);
+ // 9.5. Main-Axis Alignment
- // 9.4. Cross Size Determination
+ // 12. Distribute any remaining free space. For each flex line:
+ // 1. If the remaining free space is positive and at least one main-axis margin on this
+ // line is auto, distribute the free space equally among these margins. Otherwise,
+ // set all auto margins to zero.
+ // 2. Align the items along the main-axis per justify-content.
- // 7. Determine the hypothetical cross size of each item by performing layout with the
- // used main size and the available space, treating auto as fit-content.
+ for line in &mut flex_lines {
+ let used_space: f32 = line.items.iter().map(|child| child.target_main_size).sum();
+ let free_space = avaliable_main - used_space;
+
+ if free_space > 0.0 {
+ let mut num_auto_margins = 0;
+
+ for child in &mut line.items {
+ if child.node.main_margin_start() == style::Dimension::Auto {
+ num_auto_margins += 1;
+ }
+ if child.node.main_margin_end() == style::Dimension::Auto {
+ num_auto_margins += 1;
+ }
+ }
+
+ if num_auto_margins > 0 {
+ let margin = free_space / num_auto_margins as f32;
+
+ for child in &mut line.items {
+ if child.node.main_margin_start() == style::Dimension::Auto {
+ child.main_margin_start = margin;
+ }
+ if child.node.main_margin_end() == style::Dimension::Auto {
+ child.main_margin_end = margin;
+ }
+ }
+ } else {
+ let num_items = line.items.len();
+ let mut is_first = true;
+
+ for child in &mut line.items {
+ child.main_margin_start = child
+ .node
+ .main_margin_start()
+ .resolve(container_main_size, 0.0);
+ child.main_margin_end = child
+ .node
+ .main_margin_end()
+ .resolve(container_main_size, 0.0);
+
+ child.offset_main = match node.justify_content {
+ style::JustifyContent::FlexStart => 0.0,
+ style::JustifyContent::Center => if is_first {
+ free_space / 2.0
+ } else {
+ 0.0
+ },
+ style::JustifyContent::FlexEnd => if is_first {
+ free_space
+ } else {
+ 0.0
+ },
+ style::JustifyContent::SpaceBetween => if is_first {
+ 0.0
+ } else {
+ free_space / (num_items - 1) as f32
+ },
+ style::JustifyContent::SpaceAround => if is_first {
+ (free_space / num_items as f32) / 2.0
+ } else {
+ free_space / num_items as f32
+ },
+ style::JustifyContent::SpaceEvenly => free_space / (num_items + 1) as f32,
+ };
+
+ is_first = false;
+ }
+ }
+ }
+ }
- for line in &mut flex_lines {
- for child in &mut line.items {
- let size = compute_internal(
- child.node,
- SizeConstraint::exactly(child.target_main_size),
- SizeConstraint::between(cross.min, avaliable_cross),
- ).size;
+ // 9.6. Cross-Axis Alignment
- child.hypothetical_inner_cross_size = size.cross
- .max(child.node.min_cross_size().resolve(avaliable_cross, f32::MIN))
- .min(child.node.max_cross_size().resolve(avaliable_cross, f32::MAX))
- .max(0.0);
+ // 13. Resolve cross-axis auto margins. If a flex item has auto cross-axis margins:
+ // - If its outer cross size (treating those auto margins as zero) is less than the
+ // cross size of its flex line, distribute the difference in those sizes equally
+ // to the auto margins.
+ // - Otherwise, if the block-start or inline-start margin (whichever is in the cross axis)
+ // is auto, set it to zero. Set the opposite margin so that the outer cross size of the
+ // item equals the cross size of its flex line.
- child.hypothetical_outer_cross_size = child.hypothetical_inner_cross_size
- + node.cross_margin_start().resolve(avaliable_cross, 0.0)
- + node.cross_margin_end().resolve(avaliable_cross, 0.0);
- }
- }
-
- // 8. Calculate the cross size of each flex line.
- // If the flex container is single-line and has a definite cross size, the cross size
- // of the flex line is the flex container’s inner cross size. Otherwise, for each flex line:
- //
- // 1. Collect all the flex items whose inline-axis is parallel to the main-axis, whose
- // align-self is baseline, and whose cross-axis margins are both non-auto. Find the
- // largest of the distances between each item’s baseline and its hypothetical outer
- // cross-start edge, and the largest of the distances between each item’s baseline
- // and its hypothetical outer cross-end edge, and sum these two values.
- // 2. Among all the items not collected by the previous step, find the largest
- // outer hypothetical cross size.
- // 3. The used cross-size of the flex line is the largest of the numbers found in the
- // previous two steps and zero.
- //
- // If the flex container is single-line, then clamp the line’s cross-size to be within
- // the container’s computed min and max cross sizes. Note that if CSS 2.1’s definition
- // of min/max-width/height applied more generally, this behavior would fall out automatically.
-
- if flex_lines.len() == 1 && avaliable_cross.is_finite() {
- flex_lines[0].cross_size = avaliable_cross.min(cross.max).max(cross.min);
- } else {
for line in &mut flex_lines {
- // TODO handle baseline (1)
-
- line.cross_size = line.items.iter().map(|child| {
- child.hypothetical_outer_cross_size
- }).fold(0.0, |acc, x| acc.max(x));
+ for child in &mut line.items {
+ if child.target_cross_size < line.cross_size {
+ let free_space = line.cross_size - child.target_cross_size;
+
+ if child.node.cross_margin_start() == style::Dimension::Auto
+ && child.node.cross_margin_end() == style::Dimension::Auto
+ {
+ child.cross_margin_start = free_space / 2.0;
+ child.cross_margin_end = free_space / 2.0;
+ } else if child.node.cross_margin_start() == style::Dimension::Auto {
+ child.cross_margin_start = free_space;
+ } else if child.node.cross_margin_end() == style::Dimension::Auto {
+ child.cross_margin_end = free_space;
+ } else {
+ child.cross_margin_start = child
+ .node
+ .cross_margin_start()
+ .resolve(line.cross_size, 0.0);
+ child.cross_margin_end =
+ child.node.cross_margin_end().resolve(line.cross_size, 0.0);
+
+ // 14. Align all flex items along the cross-axis per align-self, if neither of the item’s
+ // cross-axis margins are auto.
+
+ child.offset_cross = match child.node.align_self(node) {
+ style::AlignSelf::Auto => 0.0, // Should never happen
+ style::AlignSelf::FlexStart => 0.0,
+ style::AlignSelf::FlexEnd => free_space,
+ style::AlignSelf::Center => free_space / 2.0,
+ style::AlignSelf::Baseline => free_space / 2.0, // Treat as center for now until we have baseline support
+ style::AlignSelf::Stretch => 0.0,
+ };
+ }
+ }
+ }
}
- }
- // 9. Handle 'align-content: stretch'. If the flex container has a definite cross size,
- // align-content is stretch, and the sum of the flex lines' cross sizes is less than
- // the flex container’s inner cross size, increase the cross size of each flex line
- // by equal amounts such that the sum of their cross sizes exactly equals the
- // flex container’s inner cross size.
+ // 15. Determine the flex container’s used cross size:
+ // - If the cross size property is a definite size, use that, clamped by the used
+ // min and max cross sizes of the flex container.
+ // - Otherwise, use the sum of the flex lines' cross sizes, clamped by the used
+ // min and max cross sizes of the flex container.
- if node.align_content == style::AlignContent::Stretch && avaliable_cross.is_finite() {
- let total_cross: f32 = flex_lines.iter().map(|line| line.cross_size).sum();
+ let total_cross_size = flex_lines.iter().map(|line| line.cross_size).sum();
+ let container_cross_size = if avaliable_cross.is_finite() {
+ avaliable_cross
+ } else {
+ total_cross_size
+ }.min(cross.max)
+ .max(cross.min);;
- if total_cross < avaliable_cross {
- let remaining = avaliable_cross - total_cross;
- let addition = remaining / flex_lines.len() as f32;
+ // 16. Align all flex lines per align-content.
- for line in &mut flex_lines {
- line.cross_size += addition;
- }
- }
- }
-
- // 10. Collapse visibility:collapse items. If any flex items have visibility: collapse,
- // note the cross size of the line they’re in as the item’s strut size, and restart
- // layout from the beginning.
- //
- // In this second layout round, when collecting items into lines, treat the collapsed
- // items as having zero main size. For the rest of the algorithm following that step,
- // ignore the collapsed items entirely (as if they were display:none) except that after
- // calculating the cross size of the lines, if any line’s cross size is less than the
- // largest strut size among all the collapsed items in the line, set its cross size to
- // that strut size.
- //
- // Skip this step in the second layout round.
-
- // TODO implement once (if ever) we support visibility:collapse
-
- // 11. Determine the used cross size of each flex item. If a flex item has align-self: stretch,
- // its computed cross size property is auto, and neither of its cross-axis margins are auto,
- // the used outer cross size is the used cross size of its flex line, clamped according to
- // the item’s used min and max cross sizes. Otherwise, the used cross size is the item’s
- // hypothetical cross size.
- //
- // If the flex item has align-self: stretch, redo layout for its contents, treating this
- // used size as its definite cross size so that percentage-sized children can be resolved.
- //
- // Note that this step does not affect the main size of the flex item, even if it has an
- // intrinsic aspect ratio.
-
- for line in &mut flex_lines {
- for child in &mut line.items {
- let is_stretch = child.node.align_self(node) == style::AlignSelf::Stretch;
-
- if is_stretch &&
- child.node.cross_margin_start() != style::Dimension::Auto &&
- child.node.cross_margin_end() != style::Dimension::Auto &&
- child.node.cross_size() == style::Dimension::Auto {
- child.target_cross_size = line.cross_size
- .min(child.node.max_cross_size().resolve(avaliable_cross, f32::MAX))
- .max(child.node.min_cross_size().resolve(avaliable_cross, f32::MIN));
- } else {
- child.target_cross_size = child.hypothetical_inner_cross_size;
- }
-
- if is_stretch {
- // TODO use result somehow
- compute_internal(child.node, SizeConstraint::exactly(child.target_main_size), SizeConstraint::exactly(child.target_cross_size));
- }
- }
- }
+ let free_space = container_cross_size - total_cross_size;
+ let num_lines = flex_lines.len();
+ let mut is_first = true;
- // 9.5. Main-Axis Alignment
-
- // 12. Distribute any remaining free space. For each flex line:
- // 1. If the remaining free space is positive and at least one main-axis margin on this
- // line is auto, distribute the free space equally among these margins. Otherwise,
- // set all auto margins to zero.
- // 2. Align the items along the main-axis per justify-content.
+ for line in &mut flex_lines {
+ line.offset_cross = match node.align_content {
+ style::AlignContent::FlexStart => 0.0,
+ style::AlignContent::FlexEnd => if is_first {
+ free_space
+ } else {
+ 0.0
+ },
+ style::AlignContent::Center => if is_first {
+ free_space / 2.0
+ } else {
+ 0.0
+ },
+ style::AlignContent::Stretch => 0.0,
+ style::AlignContent::SpaceBetween => if is_first {
+ 0.0
+ } else {
+ free_space / (num_lines - 1) as f32
+ },
+ style::AlignContent::SpaceAround => if is_first {
+ (free_space / num_lines as f32) / 2.0
+ } else {
+ free_space / num_lines as f32
+ },
+ };
+ is_first = false;
+ }
- for line in &mut flex_lines {
- let used_space: f32 = line.items.iter().map(|child| child.target_main_size).sum();
- let free_space = avaliable_main - used_space;
+ let mut children: Vec<layout::Node> = vec![];
- if free_space > 0.0 {
- let mut num_auto_margins = 0;
+ let mut total_offset_cross = node
+ .cross_padding_start()
+ .resolve(container_cross_size, 0.0)
+ + node.cross_border_start().resolve(container_cross_size, 0.0);
- for child in &mut line.items {
- if child.node.main_margin_start() == style::Dimension::Auto {
- num_auto_margins += 1;
- }
- if child.node.main_margin_end() == style::Dimension::Auto {
- num_auto_margins += 1;
- }
- }
+ for line in &mut flex_lines {
+ let mut total_offset_main = node.main_padding_start().resolve(container_main_size, 0.0)
+ + node.main_border_start().resolve(container_main_size, 0.0);
- if num_auto_margins > 0 {
- let margin = free_space / num_auto_margins as f32;
-
for child in &mut line.items {
- if child.node.main_margin_start() == style::Dimension::Auto {
- child.main_margin_start = margin;
- }
- if child.node.main_margin_end() == style::Dimension::Auto {
- child.main_margin_end = margin;
- }
+ let result = compute_internal(
+ child.node,
+ SizeConstraint::exactly(child.target_main_size),
+ SizeConstraint::exactly(child.target_cross_size),
+ );
+
+ let offset_main = { total_offset_main + child.offset_main + child.main_margin_start };
+
+ let offset_cross = {
+ total_offset_cross
+ + child.offset_cross
+ + line.offset_cross
+ + child.cross_margin_start
+ };
+
+ children.push(layout::Node {
+ width: if child.node.flex_direction.is_row() {
+ result.size.main
+ } else {
+ result.size.cross
+ },
+ height: if child.node.flex_direction.is_column() {
+ result.size.main
+ } else {
+ result.size.cross
+ },
+ x: if child.node.flex_direction.is_row() {
+ offset_main
+ } else {
+ offset_cross
+ },
+ y: if child.node.flex_direction.is_column() {
+ offset_main
+ } else {
+ offset_cross
+ },
+ children: result.children,
+ });
+
+ total_offset_main = offset_main + result.size.main + child.main_margin_end;
}
- } else {
- let num_items = line.items.len();
- let mut is_first = true;
- for child in &mut line.items {
- child.main_margin_start = child.node.main_margin_start().resolve(container_main_size, 0.0);
- child.main_margin_end = child.node.main_margin_end().resolve(container_main_size, 0.0);
-
- child.offset_main = match node.justify_content {
- style::JustifyContent::FlexStart => 0.0,
- style::JustifyContent::Center => if is_first { free_space / 2.0 } else { 0.0 },
- style::JustifyContent::FlexEnd => if is_first { free_space } else { 0.0 },
- style::JustifyContent::SpaceBetween => if is_first { 0.0 } else { free_space / (num_items - 1) as f32 },
- style::JustifyContent::SpaceAround => if is_first { (free_space / num_items as f32) / 2.0 } else { free_space / num_items as f32 },
- style::JustifyContent::SpaceEvenly => free_space / (num_items + 1) as f32,
- };
-
- is_first = false;
- }
- }
- }
- }
-
- // 9.6. Cross-Axis Alignment
-
- // 13. Resolve cross-axis auto margins. If a flex item has auto cross-axis margins:
- // - If its outer cross size (treating those auto margins as zero) is less than the
- // cross size of its flex line, distribute the difference in those sizes equally
- // to the auto margins.
- // - Otherwise, if the block-start or inline-start margin (whichever is in the cross axis)
- // is auto, set it to zero. Set the opposite margin so that the outer cross size of the
- // item equals the cross size of its flex line.
-
- for line in &mut flex_lines {
- for child in &mut line.items {
- if child.target_cross_size < line.cross_size {
- let free_space = line.cross_size - child.target_cross_size;
-
- if child.node.cross_margin_start() == style::Dimension::Auto && child.node.cross_margin_end() == style::Dimension::Auto {
- child.cross_margin_start = free_space / 2.0;
- child.cross_margin_end = free_space / 2.0;
- } else if child.node.cross_margin_start() == style::Dimension::Auto {
- child.cross_margin_start = free_space;
- } else if child.node.cross_margin_end() == style::Dimension::Auto {
- child.cross_margin_end = free_space;
- } else {
- child.cross_margin_start = child.node.cross_margin_start().resolve(line.cross_size, 0.0);
- child.cross_margin_end = child.node.cross_margin_end().resolve(line.cross_size, 0.0);
-
- // 14. Align all flex items along the cross-axis per align-self, if neither of the item’s
- // cross-axis margins are auto.
-
- child.offset_cross = match child.node.align_self(node) {
- style::AlignSelf::Auto => 0.0, // Should never happen
- style::AlignSelf::FlexStart => 0.0,
- style::AlignSelf::FlexEnd => free_space,
- style::AlignSelf::Center => free_space / 2.0,
- style::AlignSelf::Baseline => free_space / 2.0, // Treat as center for now until we have baseline support
- style::AlignSelf::Stretch => 0.0,
- };
- }
- }
+ total_offset_cross += line.offset_cross + line.cross_size;
}
- }
-
- // 15. Determine the flex container’s used cross size:
- // - If the cross size property is a definite size, use that, clamped by the used
- // min and max cross sizes of the flex container.
- // - Otherwise, use the sum of the flex lines' cross sizes, clamped by the used
- // min and max cross sizes of the flex container.
-
- let total_cross_size = flex_lines.iter().map(|line| line.cross_size).sum();
- let container_cross_size = if avaliable_cross.is_finite() {
- avaliable_cross
- } else {
- total_cross_size
- }.min(cross.max).max(cross.min);;
-
- // 16. Align all flex lines per align-content.
-
- let free_space = container_cross_size - total_cross_size;
- let num_lines = flex_lines.len();
- let mut is_first = true;
-
- for line in &mut flex_lines {
- line.offset_cross = match node.align_content {
- style::AlignContent::FlexStart => 0.0,
- style::AlignContent::FlexEnd => if is_first { free_space } else { 0.0 },
- style::AlignContent::Center => if is_first { free_space / 2.0 } else { 0.0 },
- style::AlignContent::Stretch => 0.0,
- style::AlignContent::SpaceBetween => if is_first { 0.0 } else { free_space / (num_lines - 1) as f32 },
- style::AlignContent::SpaceAround => if is_first { (free_space / num_lines as f32) / 2.0 } else { free_space / num_lines as f32 },
- };
- is_first = false;
- }
-
- let mut children: Vec<layout::Node> = vec![];
-
- let mut total_offset_cross =
- node.cross_padding_start().resolve(container_cross_size, 0.0) +
- node.cross_border_start().resolve(container_cross_size, 0.0);
-
- for line in &mut flex_lines {
- let mut total_offset_main =
- node.main_padding_start().resolve(container_main_size, 0.0) +
- node.main_border_start().resolve(container_main_size, 0.0);
-
- for child in &mut line.items {
- let result = compute_internal(
- child.node,
- SizeConstraint::exactly(child.target_main_size),
- SizeConstraint::exactly(child.target_cross_size));
-
- let offset_main = {
- total_offset_main +
- child.offset_main +
- child.main_margin_start
- };
-
- let offset_cross = {
- total_offset_cross +
- child.offset_cross +
- line.offset_cross +
- child.cross_margin_start
- };
-
- children.push(layout::Node {
- width: if child.node.flex_direction.is_row() { result.size.main } else { result.size.cross },
- height: if child.node.flex_direction.is_column() { result.size.main } else { result.size.cross },
- x: if child.node.flex_direction.is_row() { offset_main } else {offset_cross },
- y: if child.node.flex_direction.is_column() { offset_main } else {offset_cross },
- children: result.children,
- });
- total_offset_main = offset_main + result.size.main + child.main_margin_end;
+ ComputeResult {
+ size: FlexSize {
+ main: container_main_size,
+ cross: container_cross_size,
+ },
+ children: children,
}
-
- total_offset_cross += line.offset_cross + line.cross_size;
- }
-
- ComputeResult {
- size: FlexSize {
- main: container_main_size,
- cross: container_cross_size,
- },
- children: children,
- }
}
diff --git a/src/layout.rs b/src/layout.rs
index c9906be2c..3fe8fa74e 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -1,16 +1,16 @@
#[derive(Debug)]
pub struct Edges {
- pub left: f32,
- pub right: f32,
- pub top: f32,
- pub bottom: f32,
+ pub left: f32,
+ pub right: f32,
+ pub top: f32,
+ pub bottom: f32,
}
#[derive(Debug)]
pub struct Node {
- pub width: f32,
- pub height: f32,
- pub x: f32,
- pub y: f32,
- pub children: Vec<Node>,
-}
\ No newline at end of file
+ pub width: f32,
+ pub height: f32,
+ pub x: f32,
+ pub y: f32,
+ pub children: Vec<Node>,
+}
diff --git a/src/lib.rs b/src/lib.rs
index 87fba59fa..36c851903 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,5 @@
-pub mod style;
pub mod layout;
+pub mod style;
mod algo;
-pub use algo::compute;
\ No newline at end of file
+pub use algo::compute;
diff --git a/src/style.rs b/src/style.rs
index a48822566..a18d0be46 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -1,396 +1,418 @@
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum AlignItems {
- FlexStart,
- FlexEnd,
- Center,
- Baseline,
- Stretch,
+ FlexStart,
+ FlexEnd,
+ Center,
+ Baseline,
+ Stretch,
}
impl Default for AlignItems {
- fn default() -> AlignItems { AlignItems::Stretch }
+ fn default() -> AlignItems {
+ AlignItems::Stretch
+ }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum AlignSelf {
- Auto,
- FlexStart,
- FlexEnd,
- Center,
- Baseline,
- Stretch,
+ Auto,
+ FlexStart,
+ FlexEnd,
+ Center,
+ Baseline,
+ Stretch,
}
impl Default for AlignSelf {
- fn default() -> AlignSelf { AlignSelf::Auto }
+ fn default() -> AlignSelf {
+ AlignSelf::Auto
+ }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum AlignContent {
- FlexStart,
- FlexEnd,
- Center,
- Stretch,
- SpaceBetween,
- SpaceAround,
+ FlexStart,
+ FlexEnd,
+ Center,
+ Stretch,
+ SpaceBetween,
+ SpaceAround,
}
impl Default for AlignContent {
- fn default() -> AlignContent { AlignContent::Stretch }
+ fn default() -> AlignContent {
+ AlignContent::Stretch
+ }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Direction {
- Inherit,
- LTR,
- RTL,
+ Inherit,
+ LTR,
+ RTL,
}
impl Default for Direction {
- fn default() -> Direction { Direction::Inherit }
+ fn default() -> Direction {
+ Direction::Inherit
+ }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Display {
- Flex,
- None,
+ Flex,
+ None,
}
impl Default for Display {
- fn default() -> Display { Display::Flex }
+ fn default() -> Display {
+ Display::Flex
+ }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum FlexDirection {
- Row,
- Column,
- RowReverse,
- ColumnReverse,
+ Row,
+ Column,
+ RowReverse,
+ ColumnReverse,
}
impl Default for FlexDirection {
- fn default() -> FlexDirection { FlexDirection::Row }
+ fn default() -> FlexDirection {
+ FlexDirection::Row
+ }
}
impl FlexDirection {
- pub(crate) fn is_row(self) -> bool {
- self == FlexDirection::Row || self == FlexDirection::RowReverse
- }
+ pub(crate) fn is_row(self) -> bool {
+ self == FlexDirection::Row || self == FlexDirection::RowReverse
+ }
- pub(crate) fn is_column(self) -> bool {
- self == FlexDirection::Column || self == FlexDirection::ColumnReverse
- }
+ pub(crate) fn is_column(self) -> bool {
+ self == FlexDirection::Column || self == FlexDirection::ColumnReverse
+ }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum JustifyContent {
- FlexStart,
- Center,
- FlexEnd,
- SpaceBetween,
- SpaceAround,
- SpaceEvenly,
+ FlexStart,
+ FlexEnd,
+ Center,
+ SpaceBetween,
+ SpaceAround,
+ SpaceEvenly,
}
impl Default for JustifyContent {
- fn default() -> JustifyContent { JustifyContent::FlexStart }
+ fn default() -> JustifyContent {
+ JustifyContent::FlexStart
+ }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Overflow {
- Visible,
- Hidden,
- Scroll,
+ Visible,
+ Hidden,
+ Scroll,
}
impl Default for Overflow {
- fn default() -> Overflow { Overflow::Visible }
+ fn default() -> Overflow {
+ Overflow::Visible
+ }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Position {
- Relative,
- Absolute,
+ Relative,
+ Absolute,
}
impl Default for Position {
- fn default() -> Position { Position::Relative }
+ fn default() -> Position {
+ Position::Relative
+ }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Wrap {
- NoWrap,
- Wrap,
- WrapReverse,
+ NoWrap,
+ Wrap,
+ WrapReverse,
}
impl Default for Wrap {
- fn default() -> Wrap { Wrap::NoWrap }
+ fn default() -> Wrap {
+ Wrap::NoWrap
+ }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Dimension {
- Auto,
- Points(f32),
- Percent(f32),
+ Auto,
+ Points(f32),
+ Percent(f32),
}
impl Default for Dimension {
- fn default() -> Dimension { Dimension::Auto }
+ fn default() -> Dimension {
+ Dimension::Auto
+ }
}
impl Dimension {
- pub(crate) fn resolve(&self, parent_size: f32, auto_size: f32) -> f32 {
- match self {
- Dimension::Points(points) => *points,
- Dimension::Percent(percent) if parent_size.is_finite() => percent * parent_size,
- _ => auto_size,
+ pub(crate) fn resolve(&self, parent_size: f32, auto_size: f32) -> f32 {
+ match self {
+ Dimension::Points(points) => *points,
+ Dimension::Percent(percent) if parent_size.is_finite() => percent * parent_size,
+ _ => auto_size,
+ }
}
- }
}
#[derive(Debug)]
pub struct Edges {
- pub start: Dimension,
- pub end: Dimension,
- pub top: Dimension,
- pub bottom: Dimension,
+ pub start: Dimension,
+ pub end: Dimension,
+ pub top: Dimension,
+ pub bottom: Dimension,
}
impl Default for Edges {
- fn default() -> Edges {
- Edges {
- start: Dimension::Points(0.0),
- end: Dimension::Points(0.0),
- top: Dimension::Points(0.0),
- bottom: Dimension::Points(0.0),
- }
- }
+ fn default() -> Edges {
+ Edges {
+ start: Dimension::Points(0.0),
+ end: Dimension::Points(0.0),
+ top: Dimension::Points(0.0),
+ bottom: Dimension::Points(0.0),
+ }
+ }
}
#[derive(Debug)]
pub struct Node {
- pub position: Position,
- pub direction: Direction,
- pub flex_direction: FlexDirection,
-
- pub wrap: Wrap,
- pub overflow: Overflow,
-
- pub align_items: AlignItems,
- pub align_self: AlignSelf,
- pub align_content: AlignContent,
-
- pub justify_content: JustifyContent,
-
- pub margin: Edges,
- pub padding: Edges,
- pub border: Edges,
-
- pub flex_grow: f32,
- pub flex_shrink: f32,
- pub flex_basis: Dimension,
-
- pub left: Dimension,
- pub right: Dimension,
- pub top: Dimension,
- pub bottom: Dimension,
-
- pub width: Dimension,
- pub min_width: Dimension,
- pub max_width: Dimension,
-
- pub height: Dimension,
- pub min_height: Dimension,
- pub max_height: Dimension,
-
- pub aspect_ratio: Option<f32>,
-
- pub children: Vec<Node>,
+ pub position: Position,
+ pub direction: Direction,
+ pub flex_direction: FlexDirection,
+
+ pub wrap: Wrap,
+ pub overflow: Overflow,
+
+ pub align_items: AlignItems,
+ pub align_self: AlignSelf,
+ pub align_content: AlignContent,
+
+ pub justify_content: JustifyContent,
+
+ pub margin: Edges,
+ pub padding: Edges,
+ pub border: Edges,
+
+ pub flex_grow: f32,
+ pub flex_shrink: f32,
+ pub flex_basis: Dimension,
+
+ pub start: Dimension,
+ pub end: Dimension,
+ pub top: Dimension,
+ pub bottom: Dimension,
+
+ pub width: Dimension,
+ pub min_width: Dimension,
+ pub max_width: Dimension,
+
+ pub height: Dimension,
+ pub min_height: Dimension,
+ pub max_height: Dimension,
+
+ pub aspect_ratio: Option<f32>,
+
+ pub children: Vec<Node>,
}
impl Default for Node {
- fn default() -> Node {
- Node {
- position: Default::default(),
- direction: Default::default(),
- flex_direction: Default::default(),
+ fn default() -> Node {
+ Node {
+ position: Default::default(),
+ direction: Default::default(),
+ flex_direction: Default::default(),
- wrap: Default::default(),
- overflow: Default::default(),
+ wrap: Default::default(),
+ overflow: Default::default(),
- align_items: Default::default(),
- align_self: Default::default(),
- align_content: Default::default(),
+ align_items: Default::default(),
+ align_self: Default::default(),
+ align_content: Default::default(),
- justify_content: Default::default(),
+ justify_content: Default::default(),
- margin: Default::default(),
- padding: Default::default(),
- border: Default::default(),
+ margin: Default::default(),
+ padding: Default::default(),
+ border: Default::default(),
- flex_grow: 0.0,
- flex_shrink: 1.0,
- flex_basis: Default::default(),
+ flex_grow: 0.0,
+ flex_shrink: 1.0,
+ flex_basis: Default::default(),
- left: Default::default(),
- right: Default::default(),
- top: Default::default(),
- bottom: Default::default(),
+ start: Default::default(),
+ end: Default::default(),
+ top: Default::default(),
+ bottom: Default::default(),
- width: Default::default(),
- min_width: Default::default(),
- max_width: Default::default(),
+ width: Default::default(),
+ min_width: Default::default(),
+ max_width: Default::default(),
- height: Default::default(),
- min_height: Default::default(),
- max_height: Default::default(),
+ height: Default::default(),
+ min_height: Default::default(),
+ max_height: Default::default(),
- aspect_ratio: Default::default(),
+ aspect_ratio: Default::default(),
- children: vec![],
+ children: vec![],
+ }
}
- }
}
impl Node {
- pub(crate) fn main_size(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.width,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.height,
+ pub(crate) fn main_size(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.width,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.height,
+ }
}
- }
- pub(crate) fn min_main_size(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.min_width,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.min_height,
+ pub(crate) fn min_main_size(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.min_width,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.min_height,
+ }
}
- }
- pub(crate) fn max_main_size(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.max_width,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.max_height,
+ pub(crate) fn max_main_size(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.max_width,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.max_height,
+ }
}
- }
- pub(crate) fn main_margin_start(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.margin.start,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.top,
+ pub(crate) fn main_margin_start(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.margin.start,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.top,
+ }
}
- }
- pub(crate) fn main_margin_end(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.margin.end,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.bottom,
+ pub(crate) fn main_margin_end(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.margin.end,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.bottom,
+ }
}
- }
- pub(crate) fn main_padding_start(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.padding.start,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.top,
+ pub(crate) fn main_padding_start(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.padding.start,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.top,
+ }
}
- }
- pub(crate) fn main_padding_end(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.padding.end,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.bottom,
+ pub(crate) fn main_padding_end(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.padding.end,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.bottom,
+ }
}
- }
- pub(crate) fn main_border_start(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.border.start,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.border.top,
+ pub(crate) fn main_border_start(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.border.start,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.border.top,
+ }
}
- }
- pub(crate) fn main_border_end(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.border.end,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.border.bottom,
+ pub(crate) fn main_border_end(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.border.end,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.border.bottom,
+ }
}
- }
- pub(crate) fn cross_size(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.height,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.width,
+ pub(crate) fn cross_size(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.height,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.width,
+ }
}
- }
- pub(crate) fn min_cross_size(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.min_height,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.min_width,
+ pub(crate) fn min_cross_size(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.min_height,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.min_width,
+ }
}
- }
- pub(crate) fn max_cross_size(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.max_height,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.max_width,
+ pub(crate) fn max_cross_size(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.max_height,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.max_width,
+ }
}
- }
- pub(crate) fn cross_margin_start(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.margin.top,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.start,
+ pub(crate) fn cross_margin_start(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.margin.top,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.start,
+ }
}
- }
- pub(crate) fn cross_margin_end(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.margin.bottom,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.end,
+ pub(crate) fn cross_margin_end(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.margin.bottom,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.end,
+ }
}
- }
- pub(crate) fn cross_padding_start(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.padding.top,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.start,
+ pub(crate) fn cross_padding_start(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.padding.top,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.start,
+ }
}
- }
- pub(crate) fn cross_padding_end(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.padding.bottom,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.end,
+ pub(crate) fn cross_padding_end(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.padding.bottom,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.end,
+ }
}
- }
- pub(crate) fn cross_border_start(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.border.top,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.border.start,
+ pub(crate) fn cross_border_start(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.border.top,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.border.start,
+ }
}
- }
- pub(crate) fn cross_border_end(&self) -> Dimension {
- match self.flex_direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.border.bottom,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.border.end,
+ pub(crate) fn cross_border_end(&self) -> Dimension {
+ match self.flex_direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.border.bottom,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.border.end,
+ }
}
- }
- pub(crate) fn align_self(&self, parent: &Node) -> AlignSelf {
- if self.align_self == AlignSelf::Auto {
- match parent.align_items {
- AlignItems::FlexStart => AlignSelf::FlexStart,
- AlignItems::FlexEnd => AlignSelf::FlexEnd,
- AlignItems::Center => AlignSelf::Center,
- AlignItems::Baseline => AlignSelf::Baseline,
- AlignItems::Stretch => AlignSelf::Stretch,
- }
- } else {
- self.align_self
+ pub(crate) fn align_self(&self, parent: &Node) -> AlignSelf {
+ if self.align_self == AlignSelf::Auto {
+ match parent.align_items {
+ AlignItems::FlexStart => AlignSelf::FlexStart,
+ AlignItems::FlexEnd => AlignSelf::FlexEnd,
+ AlignItems::Center => AlignSelf::Center,
+ AlignItems::Baseline => AlignSelf::Baseline,
+ AlignItems::Stretch => AlignSelf::Stretch,
+ }
+ } else {
+ self.align_self
+ }
}
- }
}
diff --git a/test_fixtures/sample.html b/test_fixtures/sample.html
new file mode 100644
index 000000000..618f23c19
--- /dev/null
+++ b/test_fixtures/sample.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <script src="../scripts/gentest/test_helper.js"></script>
+ <link rel="stylesheet" type="text/css" href="../scripts/gentest/test_base_style.css">
+ <title>
+ Test description
+ </title>
+<head/>
+<body>
+
+<div id="test-root" style="width: 100px; height: 100px;">
+ <div style="width: 50px;"></div>
+</div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/tests/generated.rs b/tests/generated.rs
new file mode 100644
index 000000000..8d6c9c10e
--- /dev/null
+++ b/tests/generated.rs
@@ -0,0 +1,27 @@
+#[cfg(test)]
+mod generated {
+ #[test]
+ fn sample() {
+ let layout = stretch::compute(&stretch::style::Node {
+ width: stretch::style::Dimension::Points(100.0),
+ height: stretch::style::Dimension::Points(100.0),
+ children: vec![stretch::style::Node {
+ width: stretch::style::Dimension::Points(50.0),
+ children: vec![],
+ ..Default::default()
+ }],
+ ..Default::default()
+ });
+
+ assert_eq!(layout.width, 100.0);
+ assert_eq!(layout.height, 100.0);
+ assert_eq!(layout.x, 0.0);
+ assert_eq!(layout.y, 0.0);
+
+ assert_eq!(layout.children[0].width, 50.0);
+ assert_eq!(layout.children[0].height, 100.0);
+ assert_eq!(layout.children[0].x, 0.0);
+ assert_eq!(layout.children[0].y, 0.0);
+ }
+
+}
diff --git a/tests/lib.rs b/tests/manual.rs
similarity index 98%
rename from tests/lib.rs
rename to tests/manual.rs
index 7d02c7323..6a4fa0e3a 100644
--- a/tests/lib.rs
+++ b/tests/manual.rs
@@ -18,14 +18,12 @@ mod tests {
width: stretch::style::Dimension::Points(100.0),
height: stretch::style::Dimension::Points(100.0),
- children: vec![
- stretch::style::Node {
- width: stretch::style::Dimension::Percent(0.5),
+ children: vec![stretch::style::Node {
+ width: stretch::style::Dimension::Percent(0.5),
+
+ ..Default::default()
+ }],
- ..Default::default()
- },
- ],
-
..Default::default()
});
@@ -51,7 +49,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -85,7 +83,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -120,7 +118,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -157,7 +155,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -196,7 +194,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -235,7 +233,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -271,7 +269,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -307,7 +305,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -343,7 +341,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -379,7 +377,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -415,7 +413,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -452,7 +450,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -486,7 +484,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -541,7 +539,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});
@@ -607,7 +605,7 @@ mod tests {
..Default::default()
},
],
-
+
..Default::default()
});