Retain frame and extent dependencies independently

Keep the original measurement placement separate from the assigned slot.
Validate frame and extent lengths before reusing an answer or drawing, and
represent hint-only records as having no measured answer.

Retain primitive and mask coordinates with their frame/extent reference.
Forwarded children follow a reused wrapper's placement without rerunning
valid draw bodies. Keep the single Widget::draw API.

Restore the eight failing suite cases from the region/placement prototype,
with regressions for mixed coordinate references, a changed inherited
extent, the sizing-stack fraction, and an undrawn share becoming visible.

This remains experimental: nested container updates do substantially more
work than e44dea3 despite restoring the leaf and wrapper reuse guarantees.
Do not merge it as a performance improvement.
This commit is contained in:
iris-ai committed 2026-09-17 14:50:09 -04:00
1 parent 5fcace1bfa
commit efb416bbc3
14 files changed
+548 -166

No files matched your search

+20 -19
View File
@@ -1,6 +1,6 @@
use crate::{
Holds, LayerId, LayoutLen, MaskIdx, MoveIdx, PrimitiveHandle, RegionAlign, Size, TextureHandle,
UiRegion, UiVec2, WidgetId,
DrawRegion, LayerId, LayoutHolds, LayoutLen, MaskIdx, MoveIdx, RegionAlign, RetainedPrimitive,
Size, TextureHandle, UiRegion, UiVec2, WidgetId,
};
/// What is kept of a widget its parent has asked about. `drawn` says whether
@@ -15,10 +15,6 @@ pub struct ActiveData {
pub region: UiRegion,
/// Where its drawing sits inside that box, in the box's own coordinates.
pub placement: UiRegion,
/// Whether its drawing read that placement, which is what says whether
/// moving it within the region is a redraw or only a different claim on
/// the same drawing.
pub reads_placement: bool,
/// The same box as lengths of its parent's box, which is the one route
/// to a box in pixels: a draw threads these down a level at a time, and
/// [`crate::UiRenderState::redraw`] takes the same steps back up.
@@ -29,13 +25,14 @@ pub struct ActiveData {
/// asked again -- and a chain of fractions has no frame in it, which is
/// why a region node between two widgets cannot break it.
pub offer_len: UiVec2,
/// What it answered there: the size and what that held for.
pub answer: (Size, [Holds; 2]),
pub offer_placement: [Option<crate::UiSpan>; 2],
/// The measured answer and its dependencies. A hint-only dependency or
/// a widget first encountered during placement has no measurement yet.
pub answer: Option<(Size, LayoutHolds)>,
/// What the widget said it used of its box, the last time it drew.
pub size: Size,
/// The pixel lengths of `region`, per axis, that its drawing and `size`
/// hold for.
pub holds: [Holds; 2],
/// The frame, extent and explicit placement reads that this drawing holds for.
pub holds: LayoutHolds,
pub drawn: bool,
pub parent: Option<WidgetId>,
/// How far down the tree it was drawn, the root being 1. Carried down a
@@ -43,7 +40,9 @@ pub struct ActiveData {
/// widget a frame visits and cannot drift while one is being drawn.
pub depth: usize,
pub textures: Vec<TextureHandle>,
pub primitives: Vec<PrimitiveHandle>,
pub primitives: Vec<RetainedPrimitive>,
pub mask_region: Option<DrawRegion>,
pub inherited_children: Vec<WidgetId>,
pub children: Vec<WidgetId>,
/// The children whose size this widget read while drawing.
pub size_deps: Vec<WidgetId>,
@@ -74,16 +73,18 @@ pub struct ActiveData {
}
impl ActiveData {
/// Whether its drawing and size hold for a box of these pixel lengths.
pub fn holds_at(&self, px: crate::PxVec2) -> bool {
self.holds[0].contains(px.x) && self.holds[1].contains(px.y)
}
/// Whether what it answered still stands for a box of these pixel
/// lengths -- the box it was asked in, where `holds` is about the box its
/// answer then chose.
pub fn answers_at(&self, px: crate::PxVec2) -> bool {
let (_, holds) = self.answer;
holds[0].contains(px.x) && holds[1].contains(px.y)
self.answer.is_some_and(|(_, holds)| {
holds.contains(
px,
UiRegion {
x: self.offer_placement[0].unwrap_or(crate::UiSpan::FULL),
y: self.offer_placement[1].unwrap_or(crate::UiSpan::FULL),
},
)
})
}
}