Hold a clipping widget to its box, and check that it is
`Scroll` reports `LEFTOVER` on both axes because it clips its content to its box: it can neither take less of one nor honestly ask for more. `Masked` is the other widget that clips and was passing its inner's size up, so a mask over something taller than its box asked to be placed at the length it had just cut off. It reports its box now, for the same reason. The `debug_assert` the handoff has been asking for is the one that would have caught both, narrowed to what is actually true: a widget that set a mask this draw has to report inside the box it drew in. Reported as "does not exceed the box" it fires on ordinary overflow instead -- measured, a hundred fuzzer trees produce thousands of them, every one a text too tall for the box it was offered, which is what a text is meant to say. `tests/cases/scroll.rs` has a clipping widget that reports its content, to show the assertion catches it. Checked: fmt, clippy, 104 tests, all five shrinker cases at 300 seeds, 100 generated seeds, five examples byte-identical at 1920x1200. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bdab55824f
commit
95fb4f962c
3 files changed
+49
-1
No files matched your search
@@ -366,6 +366,16 @@ impl UiRenderState {
|
||||
x: rules.x.apply(size.x),
|
||||
y: rules.y.apply(size.y),
|
||||
};
|
||||
// A widget that clipped its contents to its box drew nothing outside
|
||||
// it, so reporting more than the box asks to be placed at a length it
|
||||
// does not occupy -- and its parent would place the part it cut off.
|
||||
// Overflowing is otherwise ordinary: a text too tall for the box it
|
||||
// was offered reports the height it needs.
|
||||
debug_assert!(
|
||||
mask == info.mask || AXES.into_iter().all(|axis| within_box(size, px, axis)),
|
||||
"'{}' ({id:?}) clips to {px:?} and reports {size}",
|
||||
rsc.widgets().label(id),
|
||||
);
|
||||
let holds = [own[0].and(under[0]), own[1].and(under[1])];
|
||||
debug_assert!(
|
||||
holds[0].contains(px.x) && holds[1].contains(px.y),
|
||||
@@ -1011,6 +1021,15 @@ impl UiRenderState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether what a widget reports along `axis` is inside the box it drew in.
|
||||
/// A share is a length only to whoever divides one, so it is not a claim
|
||||
/// about this box and cannot exceed it.
|
||||
fn within_box(size: Size, px: PxVec2, axis: Axis) -> bool {
|
||||
let len = size.axis(axis);
|
||||
let box_len = px.axis(axis);
|
||||
len.leftover != Weight::ZERO || box_len.mul(len.rel) + len.px <= box_len
|
||||
}
|
||||
|
||||
/// The same box is the same number of steps, both of these being lengths on
|
||||
/// the grid rather than floats to be compared for nearness.
|
||||
fn same_px(a: PxVec2, b: PxVec2) -> bool {
|
||||
|
||||
Reference in new issue
Block a user