Pin a frame by the fraction the child declared

`size_hint` resolves a child's hint against the asking widget's frame and
pins that frame, so a later draw cannot reuse a resolution made against a
different one. It asked the *resolved* hint whether it still had a fraction,
which is false whenever the frame is itself pixels -- a slot of a row, or the
box a stack's sizing child decided -- and the pin was dropped there. Ask the
declared hint, which is what made this draw depend on the frame, and what
`ruled` in `render_state` already asks for a rule.

No generated tree distinguishes the two: the fuzzer grows no `rel` rules, and
a frame that changes almost always changes a box the other pins catch. Kept
for the reason the `frame_len` pin beside it is kept -- "these two
invalidations always coincide" is an assumption nothing states.
This commit is contained in:
iris-ai committed 2026-09-19 02:51:08 -04:00
1 parent f6242aa33c
commit 38b3a81053
1 file changed
+23 -27
+23 -27
View File
@@ -267,36 +267,32 @@ impl<'a> Painter<'a> {
let widgets = self.rsc.widgets(); let widgets = self.rsc.widgets();
// A rule is the answer where there is one: it wins over whatever the // A rule is the answer where there is one: it wins over whatever the
// widget would draw, so it has to win over what the widget says too. // widget would draw, so it has to win over what the widget says too.
let hint = widgets let hint = widgets.size_rules(id.id()).axis(axis).exact().or_else(|| {
.size_rules(id.id()) widgets
.axis(axis) .get_dyn(id.id())
.exact() .and_then(|widget| widget.size_hint(axis))
.or_else(|| { });
widgets let frame = self.frame.axis(axis);
.get_dyn(id.id()) let resolved = hint.map(|hint| hint.within_len(frame));
.and_then(|widget| widget.size_hint(axis))
})
.map(|hint| hint.within_len(self.frame.axis(axis)));
#[cfg(feature = "layout-diagnostics")] #[cfg(feature = "layout-diagnostics")]
diag::hint_read(id.id(), self.id, axis, hint); {
match hint { diag::hint_read(id.id(), self.id, axis, resolved);
Some(hint) => { diag::bump(match resolved {
#[cfg(feature = "layout-diagnostics")] Some(_) => Counter::HintHits,
diag::bump(Counter::HintHits); None => Counter::HintMisses,
self.depend_on(id); });
// A fraction was just resolved against this frame, so what }
// this draw does with it is a function of the frame's length. if let Some(hint) = hint {
if hint.rel != Rel::ZERO { self.depend_on(id);
self.frame_own_len[axis as usize] = Some(self.frame.axis(axis)); // Resolving a fraction against this frame makes this draw a
} // function of the frame's length. The fraction to ask about is
Some(hint) // the child's own: resolved against a frame of pixels, none is
} // left to see it by.
None => { if hint.rel != Rel::ZERO {
#[cfg(feature = "layout-diagnostics")] self.frame_own_len[axis as usize] = Some(frame);
diag::bump(Counter::HintMisses);
None
} }
} }
resolved
} }
fn depend_on<W: ?Sized>(&mut self, child: &StrongWidget<W>) { fn depend_on<W: ?Sized>(&mut self, child: &StrongWidget<W>) {