Fix modern stylesheet and image geometry
This commit is contained in:
1 parent
5f67bad4fe
commit
c69fafae21
4 files changed
+286
-60
No files matched your search
@@ -4251,6 +4251,26 @@ fn is_full_span_column_subgrid(style: &crate::LayoutStyle) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
/// Whether an item is wholly eligible for ordinary grid auto-placement.
|
||||
///
|
||||
/// The style model retains an explicit `grid-area:auto` as `Some(Line {
|
||||
/// Auto, Auto })`, while an omitted placement stays `None`. Both represent
|
||||
/// the same indefinite row and column spans to the grid placement algorithm.
|
||||
/// Raw named placements are never auto: an unresolved name must not enter the
|
||||
/// bounded subgrid reduction merely because it has no numeric `Line` yet.
|
||||
fn has_only_auto_grid_placement(style: &crate::LayoutStyle) -> bool {
|
||||
if style.grid_column_raw.is_some() || style.grid_row_raw.is_some() {
|
||||
return false;
|
||||
}
|
||||
let axis_is_auto = |line: Option<&taffy::Line<taffy::GridPlacement>>| {
|
||||
line.is_none_or(|line| {
|
||||
matches!(line.start, taffy::GridPlacement::Auto)
|
||||
&& matches!(line.end, taffy::GridPlacement::Auto)
|
||||
})
|
||||
};
|
||||
axis_is_auto(style.grid_column.as_ref()) && axis_is_auto(style.grid_row.as_ref())
|
||||
}
|
||||
|
||||
/// Collect the deliberately bounded Grid Level 2 subset used by broad
|
||||
/// "aligned rows" components: a full-span column subgrid, optionally nested
|
||||
/// through more full-span column subgrids, whose final children auto-place one
|
||||
@@ -4331,7 +4351,9 @@ fn collect_column_subgrid_descendants(
|
||||
// This first subset intentionally excludes spanning and explicitly
|
||||
// placed items. It is the common data-row/card-row pattern and keeps
|
||||
// each descendant contribution attributable to one ancestor track.
|
||||
if child_style.grid_column.is_some()
|
||||
// Explicit `grid-area:auto` remains ordinary auto-placement in both
|
||||
// axes and is therefore equivalent to omitting all placement values.
|
||||
if !has_only_auto_grid_placement(child_style)
|
||||
|| child_style.position == Some(taffy::Position::Absolute)
|
||||
|| child_style.margin_auto[1]
|
||||
|| child_style.margin_auto[3]
|
||||
|
||||
@@ -3907,11 +3907,12 @@ fn background_image_rect(
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
let (width, height) = if let Some(size) = expression_size {
|
||||
size
|
||||
} else if let Some(size) = explicit_size {
|
||||
size
|
||||
} else if let Some(fit) = fit {
|
||||
// `cover` and `contain` are complete sizing algorithms, not contextual
|
||||
// lengths. The style layer deliberately retains the authored token string
|
||||
// for CSS math, so a keyword can also appear in `size_expression`; run the
|
||||
// typed fit first or the unresolved token path falls back to the intrinsic
|
||||
// size and silently bypasses fitting.
|
||||
let (width, height) = if let Some(fit) = fit {
|
||||
let (iw, ih) = intrinsic?;
|
||||
let scale = match fit {
|
||||
crate::ObjectFit::Cover => (box_rect.width / iw).max(box_rect.height / ih),
|
||||
@@ -3919,6 +3920,10 @@ fn background_image_rect(
|
||||
_ => 1.0,
|
||||
};
|
||||
(iw * scale, ih * scale)
|
||||
} else if let Some(size) = expression_size {
|
||||
size
|
||||
} else if let Some(size) = explicit_size {
|
||||
size
|
||||
} else {
|
||||
intrinsic.unwrap_or((box_rect.width, box_rect.height))
|
||||
};
|
||||
@@ -6394,6 +6399,38 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cover_and_contain_background_sizes_fit_the_positioning_area() {
|
||||
let image = "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='200'%20height='100'%3E%3Crect%20width='200'%20height='100'%20fill='blue'/%3E%3C/svg%3E";
|
||||
let tree = parse_html(&format!(
|
||||
r#"<html><body style="margin:0;background:white">
|
||||
<div style="position:absolute;left:0;top:0;width:100px;height:200px;
|
||||
background:red url("{image}") center/cover no-repeat"></div>
|
||||
<div style="position:absolute;left:100px;top:0;width:100px;height:200px;
|
||||
background-color:red;background-image:url("{image}");
|
||||
background-position:center;background-size:contain;background-repeat:no-repeat"></div>
|
||||
</body></html>"#
|
||||
));
|
||||
let pixmap = paint_dom(&tree, (200.0, 200.0), None).expect("pixmap");
|
||||
|
||||
let cover_edge = pixmap.pixel(50, 10).expect("cover edge");
|
||||
assert!(
|
||||
cover_edge.blue() > 200 && cover_edge.red() < 40,
|
||||
"cover must scale the 2:1 image to the owner's 200px block axis: {cover_edge:?}"
|
||||
);
|
||||
|
||||
let contain_outside = pixmap.pixel(150, 60).expect("contain letterbox");
|
||||
assert!(
|
||||
contain_outside.red() > 200 && contain_outside.blue() < 40,
|
||||
"contain must leave the area outside its centered 100x50 image unpainted: {contain_outside:?}"
|
||||
);
|
||||
let contain_center = pixmap.pixel(150, 100).expect("contain center");
|
||||
assert!(
|
||||
contain_center.blue() > 200 && contain_center.red() < 40,
|
||||
"contain must paint the fitted image through the center: {contain_center:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ordered_background_gradients_paint_every_translucent_layer() {
|
||||
let tree = parse_html(
|
||||
|
||||
@@ -2410,6 +2410,7 @@ fn nested_full_span_column_subgrid_shares_ancestor_tracks() {
|
||||
grid-column:1 / -1;
|
||||
grid-template-columns:subgrid;
|
||||
}
|
||||
.inner > * { grid-area:auto }
|
||||
.inner { column-gap:20px }
|
||||
.leaf { height:20px }
|
||||
.a { width:40px }
|
||||
@@ -2490,12 +2491,16 @@ fn unsafe_column_subgrid_cases_decline_the_fast_path() {
|
||||
}
|
||||
#narrow { width:200px }
|
||||
#partial { width:500px }
|
||||
#placed { width:500px }
|
||||
#named { width:500px }
|
||||
.subgrid, .inner {
|
||||
display:grid;
|
||||
grid-column:1 / -1;
|
||||
grid-template-columns:subgrid;
|
||||
}
|
||||
#partial-row { grid-column:1 / 3 }
|
||||
#placed-b { grid-column:2 }
|
||||
#named-b { grid-column:missing-line }
|
||||
.inner { column-gap:20px }
|
||||
.leaf { height:20px }
|
||||
.a { width:40px }
|
||||
@@ -2516,6 +2521,20 @@ fn unsafe_column_subgrid_cases_decline_the_fast_path() {
|
||||
<div class="leaf c"></div>
|
||||
</div></div>
|
||||
</div>
|
||||
<div id="placed" class="parent">
|
||||
<div class="subgrid"><div class="inner">
|
||||
<div class="leaf a"></div>
|
||||
<div id="placed-b" class="leaf b"></div>
|
||||
<div class="leaf c"></div>
|
||||
</div></div>
|
||||
</div>
|
||||
<div id="named" class="parent">
|
||||
<div class="subgrid"><div class="inner">
|
||||
<div class="leaf a"></div>
|
||||
<div id="named-b" class="leaf b"></div>
|
||||
<div class="leaf c"></div>
|
||||
</div></div>
|
||||
</div>
|
||||
"#,
|
||||
);
|
||||
let layout = layout_dom(&tree, (800.0, 600.0));
|
||||
@@ -2524,6 +2543,10 @@ fn unsafe_column_subgrid_cases_decline_the_fast_path() {
|
||||
let narrow_b = rect("narrow-b");
|
||||
let partial = rect("partial");
|
||||
let partial_b = rect("partial-b");
|
||||
let placed = rect("placed");
|
||||
let placed_b = rect("placed-b");
|
||||
let named = rect("named");
|
||||
let named_b = rect("named-b");
|
||||
|
||||
assert!(
|
||||
(narrow.height - 60.0).abs() < 0.01 && (narrow_b.x - narrow.x).abs() < 0.01,
|
||||
@@ -2533,6 +2556,16 @@ fn unsafe_column_subgrid_cases_decline_the_fast_path() {
|
||||
(partial.height - 60.0).abs() < 0.01 && (partial_b.x - partial.x).abs() < 0.01,
|
||||
"partial-span subgrid must retain the stacked fallback: {partial:?} {partial_b:?}"
|
||||
);
|
||||
assert!(
|
||||
(placed.height - 40.0).abs() < 0.01 && placed_b.x > placed.x,
|
||||
"an explicitly placed leaf must retain the existing placement fallback: \
|
||||
{placed:?} {placed_b:?}"
|
||||
);
|
||||
assert!(
|
||||
(named.height - 60.0).abs() < 0.01 && (named_b.x - named.x).abs() < 0.01,
|
||||
"an unresolved named placement must not enter the numeric subgrid reduction: \
|
||||
{named:?} {named_b:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user