Say region and placement, not extent

The split box was named `region` and `placement` on 2026-09-17; `frame`
came back as a length and survived, `extent` did not. It stayed as the
name for both halves, distinguished only by prose: `draw_at` bound the
caller's `part` to a parameter called `extent`, and `ActiveData` held two
`UiRegion`s that `draw_at` wrote `part: extent` from.

The box a parent asks a widget in is now the region, and where its
drawing ends up is its placement. `Painter`'s four holds accumulators
become the one `LayoutHolds` they were assembled into, which also drops
the name mapping between them.

The cold dump of 400 depth-5 trees is byte-identical across the change.
This commit is contained in:
iris-ai committed 2026-09-19 14:49:56 -04:00
1 parent 84dad211f5
commit 5642f2010a
13 files changed
+212 -227

No files matched your search

+59 -64
View File
@@ -22,12 +22,9 @@ pub struct Painter<'a> {
/// of. A length rather than a box, so padding can take from both the
/// frame and the box without either becoming the other.
pub(super) frame: UiVec2,
/// Where this widget's drawing goes, in its region node's coordinates.
pub(super) extent: UiRegion,
/// The extent's symbolic length where this draw read it, which makes the
/// drawing one that holds for that length alone -- the way reading a
/// length in pixels makes it hold for that number of pixels.
pub(super) extent_len: [Option<Len>; 2],
/// The box this widget was asked in, in its region node's coordinates:
/// what it draws in, and what its children's places are parts of.
pub(super) region: UiRegion,
/// The window in pixels. Frames and boxes become pixels against this one
/// unit, regardless of region-node boundaries.
pub(super) window: PxVec2,
@@ -42,14 +39,12 @@ pub struct Painter<'a> {
pub(super) children: Vec<WidgetId>,
/// The children whose size this widget read while drawing.
pub(super) size_deps: Vec<WidgetId>,
/// What this draw itself read of the window in pixels, per axis: every
/// window until it reads one, then that one, unless it says otherwise.
pub(super) window_own: [Holds; 2],
/// Its frame's symbolic length where this draw read it, which makes the
/// drawing one that holds for that frame alone.
pub(super) frame_own_len: [Option<Len>; 2],
/// The window reads' equivalent for its own box.
pub(super) extent_own: [Holds; 2],
/// What this draw itself reads, as against what its children's drawings
/// hold for: every window and every length of its own region until it
/// reads one, then that one unless it says otherwise, and the frame or
/// region length it read symbolically, each of which makes the drawing
/// hold for that length alone.
pub(super) own: LayoutHolds,
/// What each child's drawing depends on. Asking a child again replaces
/// its drawing, so it replaces this too rather than narrowing it.
pub(super) under: Vec<(WidgetId, LayoutHolds)>,
@@ -75,10 +70,10 @@ impl<'a> Painter<'a> {
self.write_resolved(kind, primitive, region, self.resolve(region));
}
/// A box in this widget's extent coordinates, composed into its region
/// node's coordinates.
/// A box in this widget's region, composed into its region node's
/// coordinates.
fn resolve(&self, region: UiRegion) -> UiRegion {
region.within(&self.extent)
region.within(&self.region)
}
fn write_resolved<P: Primitive>(
@@ -182,12 +177,12 @@ impl<'a> Painter<'a> {
let region_node = self.rsc.widgets().is_region_node(id.id());
let declared = self.declared_lens(id);
let align = self.rsc.widgets().alignment(id.id());
let (frame, extent) =
frame_and_extent(self.extent, self.frame, place, narrow, declared, align);
let (frame, region) =
frame_and_region(self.region, self.frame, place, narrow, declared, align);
#[cfg(feature = "layout-diagnostics")]
if region_node {
diag::bump(Counter::RegionNodeDraws);
diag::region_node(id.id(), self.id, extent);
diag::region_node(id.id(), self.id, region);
}
// A child listed twice would be moved twice.
let re_asked = self.children.contains(&id.id());
@@ -205,7 +200,7 @@ impl<'a> Painter<'a> {
region_node,
mask: self.mask,
frame,
part: extent,
region,
placed: place,
asked: place,
narrow,
@@ -215,8 +210,8 @@ impl<'a> Painter<'a> {
None,
self.rsc,
);
let holds = self.in_parent(holds, extent, place, narrow, declared);
let answer_holds = self.in_parent(answer_holds, extent, place, narrow, declared);
let holds = self.in_parent(holds, region, place, narrow, declared);
let answer_holds = self.in_parent(answer_holds, region, place, narrow, declared);
match self.under.iter_mut().find(|(child, _)| *child == id.id()) {
Some((_, kept)) => *kept = holds,
None => self.under.push((id.id(), holds)),
@@ -256,7 +251,7 @@ impl<'a> Painter<'a> {
fn placing(&self) -> Placing {
Placing {
id: self.id,
extent: self.extent,
region: self.region,
frame: self.frame,
window: self.window,
depth: self.depth,
@@ -303,7 +298,7 @@ impl<'a> Painter<'a> {
// the child's own: resolved against a frame of pixels, none is
// left to see it by.
if hint.rel != Rel::ZERO {
self.frame_own_len[axis as usize] = Some(frame);
self.own.frame_len[axis as usize] = Some(frame);
}
}
resolved
@@ -327,11 +322,11 @@ impl<'a> Painter<'a> {
ui.text.render(buffer, attrs, width)
}
/// Writes glyphs in the selected frame or extent coordinates.
/// Writes glyphs in the selected frame or region coordinates.
// TODO: merge the text methods into the primitive ones.
pub fn glyphs(&mut self, text: &RenderedText, origin: UiRegion) {
// Glyph offsets and sizes are pixels, which compose additively.
// Only the shared origin needs composing through the extent.
// Only the shared origin needs composing through the region.
let resolved = self.resolve(origin);
let kind = self.rsc.ui_mut().primitives.kind::<GlyphPrimitive>();
for glyph in text.glyphs.iter() {
@@ -368,9 +363,9 @@ impl<'a> Painter<'a> {
/// starts, which is what lets a container move without being drawn
/// again. One axis at a time, because a container that divides one axis
/// holds for any length of the other.
pub fn extent_len(&mut self, axis: Axis) -> Len {
let len = self.extent.axis(axis).len();
self.extent_len[axis as usize] = Some(len);
pub fn region_len(&mut self, axis: Axis) -> Len {
let len = self.region.axis(axis).len();
self.own.region_len[axis as usize] = Some(len);
len
}
@@ -378,10 +373,10 @@ impl<'a> Painter<'a> {
/// fraction it or anything under it declares is a fraction of. A
/// container reads it to hand a length of it down -- padding, which
/// takes its pixels off. Reading it pins the drawing to that frame, the
/// way [`Self::extent_len`] pins it to the box.
/// way [`Self::region_len`] pins it to the box.
pub fn frame_len(&mut self, axis: Axis) -> Len {
let len = self.frame.axis(axis);
self.frame_own_len[axis as usize] = Some(len);
self.own.frame_len[axis as usize] = Some(len);
len
}
@@ -419,13 +414,13 @@ impl<'a> Painter<'a> {
/// One axis of this widget's own box in pixels. Prefer this to
/// [`Self::px_size`] when the other axis cannot affect the drawing.
pub fn px_len(&mut self, axis: Axis) -> Px {
let part = self.extent.axis(axis).len();
let len = part.to_px(self.window.axis(axis));
let own = &mut self.extent_own[axis as usize];
let len = self.region.axis(axis).len();
let px = len.to_px(self.window.axis(axis));
let own = &mut self.own.region[axis as usize];
if *own == Holds::ANY {
*own = Holds::at(len);
*own = Holds::at(px);
}
len
px
}
/// The lengths of this widget's own box on `axis` that what it is drawing
@@ -433,15 +428,15 @@ impl<'a> Painter<'a> {
/// of the box, and the same reported size. A widget that read its length
/// in pixels holds for that one alone until it says otherwise.
pub fn holds(&mut self, axis: Axis, holds: impl Into<Holds>) {
let part = self.extent.axis(axis).len();
let len = self.region.axis(axis).len();
let holds = holds.into();
debug_assert!(
holds.contains(part.to_px(self.window.axis(axis))),
holds.contains(len.to_px(self.window.axis(axis))),
"'{}' ({:?}) says its drawing holds for lengths that leave out its own box",
self.label(),
self.id
);
self.extent_own[axis as usize] = holds;
self.own.region[axis as usize] = holds;
}
/// A window length in pixels, which is what every length in layout is
@@ -451,7 +446,7 @@ impl<'a> Painter<'a> {
pub fn to_px(&mut self, len: Len, axis: Axis) -> Px {
let window = self.window.axis(axis);
if len.rel != Rel::ZERO {
let own = &mut self.window_own[axis as usize];
let own = &mut self.own.window[axis as usize];
if *own == Holds::ANY {
*own = Holds::at(window);
}
@@ -471,7 +466,7 @@ impl<'a> Painter<'a> {
self.label(),
self.id
);
self.window_own[axis as usize] = holds;
self.own.window[axis as usize] = holds;
}
pub fn text_data(&mut self) -> &mut TextData {
@@ -568,7 +563,7 @@ impl Painter<'_> {
/// is what reached the child; where only pixels did, no length of this
/// frame can change the child's and the pin stops here.
///
/// Extent validity maps back through the part of this widget's box,
/// A child's validity maps back through the part of this widget's box,
/// where the box the child was asked in is that part; a declared length
/// places the box inside the part instead, and then only that length
/// reaches the child. A narrowed frame is not one of these: it decides
@@ -577,7 +572,7 @@ impl Painter<'_> {
fn in_parent(
&self,
holds: LayoutHolds,
extent: UiRegion,
region: UiRegion,
place: [Place; 2],
narrow: [Option<Len>; 2],
declared: [Option<LayoutLen>; 2],
@@ -600,8 +595,8 @@ impl Painter<'_> {
// wherever the part is the whole of it, and pins the same
// way.
(Part::All, false) => {
result.extent[n] = holds.extent[n];
result.extent_len[n] = holds.extent_len[n];
result.region[n] = holds.region[n];
result.region_len[n] = holds.region_len[n];
}
// Its box is a part of this widget's own box, in that box's
// own lengths, so what it holds for maps back through that
@@ -612,10 +607,10 @@ impl Painter<'_> {
// widget's own length.
(Part::Of(span), false) => {
let part_len = span.len();
result.extent[n] = holds.extent[n].through(part_len);
result.extent_len[n] = holds.extent_len[n].map(|pinned| match part_len.rel {
result.region[n] = holds.region[n].through(part_len);
result.region_len[n] = holds.region_len[n].map(|pinned| match part_len.rel {
Rel::ONE => pinned - Len::from_parts(Rel::ZERO, part_len.px),
_ => self.extent.axis(axis).len(),
_ => self.region.axis(axis).len(),
});
}
// Its box is a length this widget decided, from its own
@@ -624,7 +619,7 @@ impl Painter<'_> {
// on the window and none of it on that box.
_ => {
result.window[n] =
result.window[n].and(holds.extent[n].through(extent.axis(axis).len()));
result.window[n].and(holds.region[n].through(region.axis(axis).len()));
}
}
}
@@ -672,14 +667,14 @@ pub(crate) fn fills(reported: LayoutLen, declared: Option<LayoutLen>, decided: b
/// That is what makes a fraction the same fraction wherever the part it is
/// placed in sits and however long it is -- the fraction is resolved once,
/// here, against the frame it was reported of.
pub(crate) fn placed_extent(
part: UiRegion,
pub(crate) fn placement(
region: UiRegion,
size: Size,
declared: [Option<LayoutLen>; 2],
fill: [bool; 2],
align: RegionAlign,
) -> UiRegion {
let mut placed = part;
let mut placed = region;
for axis in AXES {
let n = axis as usize;
let reported = size.axis(axis);
@@ -705,7 +700,7 @@ pub(crate) fn placed_extent(
/// can name. The child's declaration is a fraction of whichever reached it,
/// and is the only one of the three that also places the box: a box the
/// caller decided is what `place` names.
pub(crate) fn frame_and_extent(
pub(crate) fn frame_and_region(
own: UiRegion,
parent_frame: UiVec2,
place: [Place; 2],
@@ -713,9 +708,9 @@ pub(crate) fn frame_and_extent(
declared: [Option<LayoutLen>; 2],
align: RegionAlign,
) -> (UiVec2, UiRegion) {
let part = part_of(own, place, align);
let given = region_of(own, place, align);
let mut frame = parent_frame;
let mut extent = part;
let mut region = given;
for axis in AXES {
let n = axis as usize;
let sized = match place[n].part() {
@@ -730,22 +725,22 @@ pub(crate) fn frame_and_extent(
.unwrap_or(base);
*frame.axis_mut(axis) = len;
if declared[n].is_some() {
let slot = part.axis(axis);
let slot = given.axis(axis);
let start = slot.start + (slot.len() - len).scale(align.axis(axis).rel());
*extent.axis_mut(axis) = UiSpan::new(start, start + len);
*region.axis_mut(axis) = UiSpan::new(start, start + len);
}
}
(frame, extent)
(frame, region)
}
/// The part of a widget's own box a `place` names, in the coordinates that
/// box is in.
fn part_of(extent: UiRegion, place: [Place; 2], align: RegionAlign) -> UiRegion {
let mut part = extent;
fn region_of(own: UiRegion, place: [Place; 2], align: RegionAlign) -> UiRegion {
let mut region = own;
for axis in AXES {
*part.axis_mut(axis) = place[axis as usize]
*region.axis_mut(axis) = place[axis as usize]
.part()
.of(*extent.axis(axis), align.axis(axis));
.of(*own.axis(axis), align.axis(axis));
}
part
region
}