Place a locally redrawn widget in its box, not just at its length
A dirty widget is asked again in the box its parent asked it in, and then again in the box its parent chose from that answer. The second ask was skipped whenever the two boxes were the same *length*, which is not the same question: an offer as long as the final box but somewhere else is a different box. `d3b0ebf` already compared whole boxes for `parent_must_place` and left this one a length comparison, so the two halves of one decision disagreed. It shows on a region node, which draws the box it drew in into its own move entry. A scroll inside a scrolled span is offered the outer scroll's whole viewport and placed 24px above it, the height of the sized child the outer scroll snaps to the end of; redrawing only its text left it at the offer and 24px too low. `tests/cases/unsettled.rs` had that five-widget tree ignored as a known defect and now runs it. `px_region` names the walk both comparisons and `window_region` were writing out. Checked: fmt, clippy, 80 tests, the release oracle at 100 seeds, all fifteen shrinker cases at 400 seeds of depth 5, and `tabs`, `view`, `minimal`, `text`, `random` plus the tab replay byte-identical at 1920x1200 against `98d4e98`. The `many` fixture's twenty-five counters are unchanged. Fixed with it, from the handoff's unreduced leads: shrinker seeds 174 and 175 on `repaint-some` and seed 2 on `region-node`, and oracle seeds 18 and 190 at depth 6. Still failing: shrinker seed 288 on `region-node`, and oracle seed 326 at depth 6, which reduces to 43 widgets around two `Branch`es and is not this. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
98d4e98a29
commit
aea878d141
2 files changed
+32
-31
No files matched your search
+19
-18
@@ -468,6 +468,12 @@ impl UiRenderState {
|
||||
.to_px(self.output_size)
|
||||
}
|
||||
|
||||
/// Where a region held in `slot`'s coordinates lands on screen, which is
|
||||
/// the walk the vertex shader does.
|
||||
fn px_region(&self, slot: MoveIdx, region: UiRegion) -> PixelRegion {
|
||||
self.moves.resolve(slot, region).to_px(self.output_size)
|
||||
}
|
||||
|
||||
/// A clean widget's retained answer, if that answer holds for a box of
|
||||
/// `px`. This does not move its drawing, which may already be in the box
|
||||
/// that answer placed it in.
|
||||
@@ -903,15 +909,12 @@ impl UiRenderState {
|
||||
}
|
||||
|
||||
/// Where a widget is on screen: its box composed through the boxes it
|
||||
/// sits within, which is the walk the vertex shader does. `None` for one
|
||||
/// that is not drawn.
|
||||
/// sits within. `None` for one that is not drawn.
|
||||
pub fn window_region(&self, id: &impl IdLike) -> Option<PixelRegion> {
|
||||
let active = self.active.get(&id.id())?;
|
||||
if !active.drawn {
|
||||
return None;
|
||||
}
|
||||
let region = self.moves.resolve(active.parent_move, active.region);
|
||||
Some(region.to_px(self.output_size))
|
||||
active
|
||||
.drawn
|
||||
.then(|| self.px_region(active.parent_move, active.region))
|
||||
}
|
||||
|
||||
/// Settles a dirty widget: asks it again where its parent asked, and
|
||||
@@ -955,17 +958,15 @@ impl UiRenderState {
|
||||
None => Self::root_region(id, rsc.widgets()),
|
||||
};
|
||||
let offered_px = self.px_of(active.parent_move, asked_in);
|
||||
let at_offer = same_px(self.px_of(active.parent_move, region), offered_px);
|
||||
let parent_must_place = active.parent.is_some()
|
||||
&& (!region_node || active.align_override)
|
||||
&& !same_pixel_region(
|
||||
self.moves
|
||||
.resolve(active.parent_move, region)
|
||||
.to_px(self.output_size),
|
||||
self.moves
|
||||
.resolve(active.parent_move, asked_in)
|
||||
.to_px(self.output_size),
|
||||
);
|
||||
// Whole boxes rather than lengths: an offer as long as the final box
|
||||
// but somewhere else is a different box, and a region node drawing at
|
||||
// its offer writes the box it drew in into its own entry.
|
||||
let at_offer = same_pixel_region(
|
||||
self.px_region(active.parent_move, region),
|
||||
self.px_region(active.parent_move, asked_in),
|
||||
);
|
||||
let parent_must_place =
|
||||
active.parent.is_some() && (!region_node || active.align_override) && !at_offer;
|
||||
// An independently positioned region node can redraw at its offer
|
||||
// and move its slot to its own placement. Every other widget needs
|
||||
// its parent to reproduce a different final position.
|
||||
|
||||
Reference in new issue
Block a user