Fix sizing of quadratic shapes (square/circle) (#5451)
Co-authored-by: Laurenz <laurmaedje@gmail.com> Co-authored-by: PgBiel <9021226+PgBiel@users.noreply.github.com>
@ -348,29 +348,48 @@ fn layout_shape(
|
|||||||
pod.size = crate::pad::shrink(region.size, &inset);
|
pod.size = crate::pad::shrink(region.size, &inset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layout the child.
|
// If the shape is quadratic, we first measure it to determine its size
|
||||||
frame = crate::layout_frame(engine, child, locator.relayout(), styles, pod)?;
|
// and then layout with full expansion to force the aspect ratio and
|
||||||
|
// make sure it's really quadratic.
|
||||||
// If the child is a square or circle, relayout with full expansion into
|
|
||||||
// square region to make sure the result is really quadratic.
|
|
||||||
if kind.is_quadratic() {
|
if kind.is_quadratic() {
|
||||||
let length = frame.size().max_by_side().min(pod.size.min_by_side());
|
let length = match quadratic_size(pod) {
|
||||||
let quad_pod = Region::new(Size::splat(length), Axes::splat(true));
|
Some(length) => length,
|
||||||
frame = crate::layout_frame(engine, child, locator, styles, quad_pod)?;
|
None => {
|
||||||
|
// Take as much as the child wants, but without overflowing.
|
||||||
|
crate::layout_frame(engine, child, locator.relayout(), styles, pod)?
|
||||||
|
.size()
|
||||||
|
.max_by_side()
|
||||||
|
.min(pod.size.min_by_side())
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pod = Region::new(Size::splat(length), Axes::splat(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layout the child.
|
||||||
|
frame = crate::layout_frame(engine, child, locator, styles, pod)?;
|
||||||
|
|
||||||
// Apply the inset.
|
// Apply the inset.
|
||||||
if has_inset {
|
if has_inset {
|
||||||
crate::pad::grow(&mut frame, &inset);
|
crate::pad::grow(&mut frame, &inset);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The default size that a shape takes on if it has no child and
|
// The default size that a shape takes on if it has no child and no
|
||||||
// enough space.
|
// forced sizes.
|
||||||
let default = Size::new(Abs::pt(45.0), Abs::pt(30.0));
|
let default = Size::new(Abs::pt(45.0), Abs::pt(30.0)).min(region.size);
|
||||||
let mut size = region.expand.select(region.size, default.min(region.size));
|
|
||||||
if kind.is_quadratic() {
|
let size = if kind.is_quadratic() {
|
||||||
size = Size::splat(size.min_by_side());
|
Size::splat(match quadratic_size(region) {
|
||||||
}
|
Some(length) => length,
|
||||||
|
None => default.min_by_side(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// For each dimension, pick the region size if forced, otherwise
|
||||||
|
// use the default size (or the region size if the default
|
||||||
|
// is too large for the region).
|
||||||
|
region.expand.select(region.size, default)
|
||||||
|
};
|
||||||
|
|
||||||
frame = Frame::soft(size);
|
frame = Frame::soft(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,6 +430,24 @@ fn layout_shape(
|
|||||||
Ok(frame)
|
Ok(frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines the forced size of a quadratic shape based on the region, if any.
|
||||||
|
///
|
||||||
|
/// The size is forced if at least one axis is expanded because `expand` is
|
||||||
|
/// `true` for axes whose size was manually specified by the user.
|
||||||
|
fn quadratic_size(region: Region) -> Option<Abs> {
|
||||||
|
if region.expand.x && region.expand.y {
|
||||||
|
// If both `width` and `height` are specified, we choose the
|
||||||
|
// smaller one.
|
||||||
|
Some(region.size.x.min(region.size.y))
|
||||||
|
} else if region.expand.x {
|
||||||
|
Some(region.size.x)
|
||||||
|
} else if region.expand.y {
|
||||||
|
Some(region.size.y)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new rectangle as a path.
|
/// Creates a new rectangle as a path.
|
||||||
pub fn clip_rect(
|
pub fn clip_rect(
|
||||||
size: Size,
|
size: Size,
|
||||||
|
BIN
tests/ref/circle-beyond-page-width-overflows.png
Normal file
After Width: | Height: | Size: 620 B |
BIN
tests/ref/circle-size-beyond-default.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
tests/ref/rect-size-beyond-default.png
Normal file
After Width: | Height: | Size: 185 B |
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 290 B |
BIN
tests/ref/square-overflow-forced-height.png
Normal file
After Width: | Height: | Size: 468 B |
BIN
tests/ref/square-overflow-forced-width.png
Normal file
After Width: | Height: | Size: 457 B |
BIN
tests/ref/square-size-beyond-default.png
Normal file
After Width: | Height: | Size: 198 B |
@ -67,3 +67,15 @@ Expanded by height.
|
|||||||
circle(width: 10%),
|
circle(width: 10%),
|
||||||
circle(height: 50%),
|
circle(height: 50%),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
--- circle-size-beyond-default ---
|
||||||
|
// Test that setting a circle's height beyond its default sizes it correctly.
|
||||||
|
#circle()
|
||||||
|
#circle(height: 60pt)
|
||||||
|
#circle(width: 60pt)
|
||||||
|
#circle(radius: 30pt)
|
||||||
|
|
||||||
|
--- circle-beyond-page-width-overflows ---
|
||||||
|
// Test that sizing a circle beyond the page width correctly overflows the page.
|
||||||
|
#set page(height: 100pt)
|
||||||
|
#circle(width: 150%)
|
||||||
|
@ -105,3 +105,9 @@
|
|||||||
#align(right, rect(width: -1cm, fill: gradient.linear(red, blue))[Reverse right])
|
#align(right, rect(width: -1cm, fill: gradient.linear(red, blue))[Reverse right])
|
||||||
|
|
||||||
#align(right, rect(width: 1cm, fill: gradient.linear(red, blue))[Right])
|
#align(right, rect(width: 1cm, fill: gradient.linear(red, blue))[Right])
|
||||||
|
|
||||||
|
--- rect-size-beyond-default ---
|
||||||
|
// Test that setting a rectangle's height beyond its default sizes it correctly.
|
||||||
|
#rect()
|
||||||
|
#rect(height: 60pt)
|
||||||
|
#rect(width: 60pt)
|
||||||
|
@ -77,11 +77,25 @@
|
|||||||
#set page(width: 20pt, height: 10pt, margin: 0pt)
|
#set page(width: 20pt, height: 10pt, margin: 0pt)
|
||||||
#stack(dir: ltr, square(fill: forest), square(fill: conifer))
|
#stack(dir: ltr, square(fill: forest), square(fill: conifer))
|
||||||
|
|
||||||
--- square-overflow ---
|
--- square-no-overflow ---
|
||||||
// Test that square doesn't overflow due to its aspect ratio.
|
// Test that square doesn't overflow due to its aspect ratio.
|
||||||
#set page(width: 40pt, height: 25pt, margin: 5pt)
|
#set page(width: 40pt, height: 25pt, margin: 5pt)
|
||||||
#square(width: 100%)
|
#square()
|
||||||
#square(width: 100%)[Hello there]
|
#square[Hello there]
|
||||||
|
|
||||||
|
--- square-overflow-forced-width ---
|
||||||
|
// Test that a width-overflowing square is laid out regardless of the
|
||||||
|
// presence of inner content.
|
||||||
|
#set page(width: 60pt, height: 100pt)
|
||||||
|
#square(width: 150%)
|
||||||
|
#square(width: 150%)[Hello there]
|
||||||
|
|
||||||
|
--- square-overflow-forced-height ---
|
||||||
|
// Test that a height-overflowing square is laid out regardless of the
|
||||||
|
// presence of inner content.
|
||||||
|
#set page(width: 120pt, height: 60pt)
|
||||||
|
#square(height: 150%)
|
||||||
|
#square(height: 150%)[Hello there]
|
||||||
|
|
||||||
--- square-size-relative-invalid ---
|
--- square-size-relative-invalid ---
|
||||||
// Size cannot be relative because we wouldn't know
|
// Size cannot be relative because we wouldn't know
|
||||||
@ -144,3 +158,10 @@
|
|||||||
// Test that square sets correct base for its content.
|
// Test that square sets correct base for its content.
|
||||||
#set page(height: 80pt)
|
#set page(height: 80pt)
|
||||||
#square(width: 40%, rect(width: 60%, height: 80%))
|
#square(width: 40%, rect(width: 60%, height: 80%))
|
||||||
|
|
||||||
|
--- square-size-beyond-default ---
|
||||||
|
// Test that setting a square's height beyond its default sizes it correctly.
|
||||||
|
#square()
|
||||||
|
#square(height: 60pt)
|
||||||
|
#square(width: 60pt)
|
||||||
|
#square(size: 60pt)
|
||||||
|