Skip to content

Commit 64f8aa0

Browse files
authored
Compute content size, padding and border for each node (#573)
* Add content_size field to Layout struct * Add scroll_width and scroll_height methods to Layout struct * Add content_size field to LayoutOutput struct * Implement content size computation for flexbox * Implement content size computation for CSS Grid * Add Overflow::Clip style * Add tests for scroll width/height * Use naive dimensions for scroll size tests * Round content size * Print content size in print_tree function * Trim text content of text nodes in generated tests * Remove caching run_mode exception for leaf layout * Leaf layout: always compute content size when run mode is PerformLayout * Set content_size of root node * Flexbox: compute correct content size * Grid: compute correct content size * Block: compute correct content size * Remove unsupported non-breaking spaces from leaf tests * Document feature flags * Add content_size feature flag * Feature flag Flexbox content_size implementation * Feature flag Grid content_size implementation * Feature flag content_size assertions in gentests * Add benchmark feature flag for content size * Simplify f32_min and f32_max implementations * Enable content_size feature by default * Fixup: format fixtures * Fix clippy lints * Introduce Size::has_non_zero_area method * Remove commented code from test_helper script * Refactor main/cross -> x/y conversion * Fully feature flag content size computation * Disable content_size feature by default * Fix Taffy.layout method when rounding is disabled * Use absolute difference when comparing unrounded values in gentests * Output scrollbar size in layout * Fix content_size feature flag in print tree function * Update scroll_width/scroll_height methods to take into account space taken up by scrollbars * Specialise cache for perform_layout and run_mode steps * Fix typo: it's -> its * Add padding and border fields to Layout struct * Use border in scroll width/height calculation * Make content_size a default feature * Document content size code
1 parent 70b3571 commit 64f8aa0

1,014 files changed

Lines changed: 82073 additions & 4251 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ jobs:
5656
- run: cargo build --features serde
5757
- run: cargo test --features serde
5858

59+
test-features-default-except-content-size:
60+
name: "Test Suite [Features: Default except content_size]"
61+
runs-on: ubuntu-latest
62+
steps:
63+
- uses: actions/checkout@v4
64+
- uses: dtolnay/rust-toolchain@stable
65+
- run: cargo build --no-default-features --features std,taffy_tree,grid,flexbox,block_layout
66+
- run: cargo test --no-default-features --features std,taffy_tree,grid,flexbox,block_layout
67+
5968
test-features-no-grid-nor-flexbox:
6069
name: "Test Suite [Features: std (no grid or flexbox)]"
6170
runs-on: ubuntu-latest

Cargo.toml

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,39 @@ serde = { version = "1.0", optional = true, features = ["serde_derive"] }
2121
slotmap = { version = "1.0.6", optional = true }
2222
grid = { version = "0.11.0", default-features = false, optional = true }
2323

24+
### FEATURES #################################################################
25+
2426
[features]
25-
default = ["std", "flexbox", "grid", "block_layout", "taffy_tree"]
27+
default = ["std", "taffy_tree", "flexbox", "grid", "block_layout", "content_size"]
28+
29+
### Algorithms
30+
31+
# Enables the Block layout algorithm
2632
block_layout = []
33+
# Enables the Flexbox layout algorithm
2734
flexbox = []
35+
# Enables the CSS Grid layout algorithm
2836
grid = ["alloc", "dep:grid"]
29-
alloc = []
30-
std = ["num-traits/std", "grid?/std"]
37+
# Causes all algorithms to compute and output a content size for each node
38+
content_size = []
39+
40+
### Taffy Tree
41+
42+
# Enable the built-in Taffy node tree
43+
taffy_tree = ["dep:slotmap"]
44+
45+
### Other
46+
47+
# Add serde derives to Style structs
3148
serde = ["dep:serde"]
49+
# Allow Taffy to depend on the standard library
50+
std = ["num-traits/std", "grid?/std"]
51+
# Allow Taffy to depend on the alloc library
52+
alloc = []
53+
# Internal featyre for debugging
3254
debug = ["std"]
55+
# Internal feature for profiling
3356
profile = ["std"]
34-
taffy_tree = ["dep:slotmap"]
3557

3658
[dev-dependencies]
3759
serde_json = "1.0.93"

benches/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ slotmap = { version = "1.0.6", optional = true }
2626
yoga = ["dep:yoga", "dep:slotmap", "dep:ordered-float"]
2727
yoga-super-deep = ["yoga"]
2828
taffy03 = ["dep:taffy_03"]
29+
content_size = ["taffy/content_size"]
2930
small = []
3031
large = []
3132

scripts/gentest/src/main.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,15 @@ fn generate_assertions(ident: &str, node: &Value, use_rounding: bool) -> TokenSt
234234
let layout = if use_rounding { &node["smartRoundedLayout"] } else { &node["unroundedLayout"] };
235235

236236
let read_f32 = |s: &str| layout[s].as_f64().unwrap() as f32;
237+
let read_naive_f32 = |s: &str| node["naivelyRoundedLayout"][s].as_f64().unwrap() as f32;
237238
let width = read_f32("width");
238239
let height = read_f32("height");
239240
let x = read_f32("x");
240241
let y = read_f32("y");
241242

243+
let scroll_width = (read_f32("scrollWidth") - read_naive_f32("clientWidth")).max(0.0);
244+
let scroll_height = (read_f32("scrollHeight") - read_naive_f32("clientHeight")).max(0.0);
245+
242246
let children = {
243247
let mut c = Vec::new();
244248
if let Value::Array(ref value) = node["children"] {
@@ -253,21 +257,31 @@ fn generate_assertions(ident: &str, node: &Value, use_rounding: bool) -> TokenSt
253257

254258
if use_rounding {
255259
quote!(
256-
let Layout { size, location, .. } = taffy.layout(#ident).unwrap();
260+
#[cfg_attr(not(feature = "content_size"), allow(unused_variables))]
261+
let layout @ Layout { size, location, .. } = taffy.layout(#ident).unwrap();
257262
assert_eq!(size.width, #width, "width of node {:?}. Expected {}. Actual {}", #ident, #width, size.width);
258263
assert_eq!(size.height, #height, "height of node {:?}. Expected {}. Actual {}", #ident, #height, size.height);
259264
assert_eq!(location.x, #x, "x of node {:?}. Expected {}. Actual {}", #ident, #x, location.x);
260265
assert_eq!(location.y, #y, "y of node {:?}. Expected {}. Actual {}", #ident, #y, location.y);
266+
#[cfg(feature = "content_size")]
267+
assert_eq!(layout.scroll_width(), #scroll_width, "scroll_width of node {:?}. Expected {}. Actual {}", #ident, #scroll_width, layout.scroll_width());
268+
#[cfg(feature = "content_size")]
269+
assert_eq!(layout.scroll_height(), #scroll_height, "scroll_height of node {:?}. Expected {}. Actual {}", #ident, #scroll_height, layout.scroll_height());
261270

262271
#children
263272
)
264273
} else {
265274
quote!(
266-
let Layout { size, location, .. } = taffy.layout(#ident).unwrap();
267-
assert!(size.width - #width < 0.1, "width of node {:?}. Expected {}. Actual {}", #ident, #width, size.width);
268-
assert!(size.height - #height < 0.1, "height of node {:?}. Expected {}. Actual {}", #ident, #height, size.height);
269-
assert!(location.x - #x < 0.1, "x of node {:?}. Expected {}. Actual {}", #ident, #x, location.x);
270-
assert!(location.y - #y < 0.1, "y of node {:?}. Expected {}. Actual {}", #ident, #y, location.y);
275+
#[cfg_attr(not(feature = "content_size"), allow(unused_variables))]
276+
let layout @ Layout { size, location, .. } = taffy.layout(#ident).unwrap();
277+
assert!((size.width - #width).abs() < 0.1, "width of node {:?}. Expected {}. Actual {}", #ident, #width, size.width);
278+
assert!((size.height - #height).abs() < 0.1, "height of node {:?}. Expected {}. Actual {}", #ident, #height, size.height);
279+
assert!((location.x - #x).abs() < 0.1, "x of node {:?}. Expected {}. Actual {}", #ident, #x, location.x);
280+
assert!((location.y - #y).abs() < 0.1, "y of node {:?}. Expected {}. Actual {}", #ident, #y, location.y);
281+
#[cfg(feature = "content_size")]
282+
assert!((layout.scroll_width() - #scroll_width).abs() < 0.1, "scroll_width of node {:?}. Expected {}. Actual {}", #ident, #scroll_width, layout.scroll_width());
283+
#[cfg(feature = "content_size")]
284+
assert!((layout.scroll_height() - #scroll_height).abs() < 0.1, "scroll_height of node {:?}. Expected {}. Actual {}", #ident, #scroll_height, layout.scroll_height());
271285

272286
#children
273287
)
@@ -867,6 +881,8 @@ fn generate_scalar_definition(track_definition: &serde_json::Map<String, Value>)
867881
}
868882

869883
fn generate_node_context(text_content: &str, writing_mode: Option<&str>, aspect_ratio: Option<f32>) -> TokenStream {
884+
let trimmed_text_content = text_content.trim();
885+
870886
let writing_mode_token = match writing_mode {
871887
Some("vertical-rl" | "vertical-lr") => quote!(crate::WritingMode::Vertical),
872888
_ => quote!(crate::WritingMode::Horizontal),
@@ -879,7 +895,7 @@ fn generate_node_context(text_content: &str, writing_mode: Option<&str>, aspect_
879895

880896
quote!(
881897
crate::TextMeasure {
882-
text_content: #text_content,
898+
text_content: #trimmed_text_content,
883899
writing_mode: #writing_mode_token,
884900
_aspect_ratio: #aspect_ratio_token,
885901
}

scripts/gentest/test_helper.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,10 @@ function describeElement(e) {
284284
height: boundingRect.height,
285285
x: boundingRect.x - parentBoundingRect.x,
286286
y: boundingRect.y - parentBoundingRect.y,
287+
scrollWidth: e.scrollWidth,
288+
scrollHeight: e.scrollHeight,
289+
clientWidth: e.clientWidth,
290+
clientHeight: e.clientHeight,
287291
},
288292

289293
// The naively rounded layout of the node. This is equivalent to calling Math.round() on
@@ -293,6 +297,10 @@ function describeElement(e) {
293297
height: e.offsetHeight,
294298
x: e.offsetLeft + e.parentNode.clientLeft,
295299
y: e.offsetTop + e.parentNode.clientTop,
300+
scrollWidth: e.scrollWidth,
301+
scrollHeight: e.scrollHeight,
302+
clientWidth: e.clientWidth,
303+
clientHeight: e.clientHeight,
296304
},
297305

298306
// The naive rounding can result in 1px gaps in the layout, so Taffy uses a smarter algorithm to avoid this.
@@ -303,6 +311,10 @@ function describeElement(e) {
303311
height: Math.round(boundingRect.bottom) - Math.round(boundingRect.top),
304312
x: Math.round(boundingRect.x - parentBoundingRect.x),
305313
y: Math.round(boundingRect.y - parentBoundingRect.y),
314+
scrollWidth: e.scrollWidth,
315+
scrollHeight: e.scrollHeight,
316+
clientWidth: e.clientWidth,
317+
clientHeight: e.clientHeight,
306318
},
307319

308320
// Whether the test should enable rounding

0 commit comments

Comments
 (0)