Match a place's own cases instead of asking it five questions

`PlaceSpan` and `RelBase` were private to `place.rs`, so `painter.rs`
reached them through six `pub(crate)` accessors -- `stated_rel_base`,
`narrows_rel_base`, `within_span`, `is_sized`, `does_fill`,
`with_rel_base` -- and `in_parent` re-derived a three-case enum from
five yes/no answers. The two enums are `pub` now and `ui/mod.rs`
re-exports `place` by name rather than by glob, the way it already did
for `painter`, so nothing new leaves the crate and there is no
visibility qualifier to get right. All six accessors are deleted:
`in_parent` matches `(at.span, declared)`, `resolve_rel_base` matches
`(rel_base, span)` and assigns, and the other three read the field.

`!at.is_sized()` was dead. `PlaceSpan::Sized` is built in exactly one
place, `Len::as_desc`, which sets `RelBase::Len(self)` in the same
literal, so `stated_rel_base().is_none()` already excluded it. Deleting
`with_rel_base` removes the only writer that could have separated the
two, so a named length now carries its own base by construction rather
than by habit -- which is what the comment on `RelBase` says.

The four `pub(crate)` methods left in `painter.rs` are inherent methods
on types the crate does export, so hiding the path is not available to
them; they are `pub(super)`, which is the module tree that calls them.

fmt, workspace clippy under `-D warnings` with and without
`layout-diagnostics`, and the workspace tests under both are clean. The
cold dump over 400 depth-5 trees is byte-identical: 34,492 boxes.
This commit is contained in:
iris-ai committed 2026-09-19 23:51:58 -04:00
1 parent 7e2b4cd9db
commit 1ebd4d3a05
3 files changed
+43 -79

No files matched your search

+1 -1
View File
@@ -20,7 +20,7 @@ pub use active::*;
pub use holds::*; pub use holds::*;
pub use layout_holds::*; pub use layout_holds::*;
pub use painter::{Painter, PrimitiveLike}; pub use painter::{Painter, PrimitiveLike};
pub use place::*; pub use place::{PlaceDesc, PlaceDescAxis, RetainedPrimitive};
pub use render_state::*; pub use render_state::*;
#[derive(Default)] #[derive(Default)]
+32 -20
View File
@@ -8,7 +8,10 @@ use crate::{
GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveInst, PrimitiveKind, GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveInst, PrimitiveKind,
TexturePrimitive, TexturePrimitive,
}, },
ui::render_state::{DrawInfo, Placing}, ui::{
place::{PlaceSpan, RelBase},
render_state::{DrawInfo, Placing},
},
}; };
/// makes your surfaces look pretty /// makes your surfaces look pretty
@@ -156,11 +159,15 @@ impl<'a> Painter<'a> {
/// only moves its child does not pin its drawing to a rel base. /// only moves its child does not pin its drawing to a rel base.
fn resolve_rel_base(&mut self, mut place: PlaceDesc) -> PlaceDesc { fn resolve_rel_base(&mut self, mut place: PlaceDesc) -> PlaceDesc {
for axis in Axis::BOTH { for axis in Axis::BOTH {
if let Some(span) = place[axis].narrows_rel_base() { let at = &mut place[axis];
let len = span.len(); let (RelBase::WithRegion, PlaceSpan::Within(span)) = (at.rel_base, at.span) else {
let stated = (len != Len::FULL).then(|| len.within_len(self.rel_base(axis))); continue;
place[axis] = place[axis].with_rel_base(stated); };
} let len = span.len();
at.rel_base = match len == Len::FULL {
true => RelBase::Inherit,
false => RelBase::Len(len.within_len(self.rel_base(axis))),
};
} }
place place
} }
@@ -255,7 +262,7 @@ impl<'a> Painter<'a> {
let place = self.resolve_rel_base(place.into()); let place = self.resolve_rel_base(place.into());
let states_rel_base = Axis::BOTH let states_rel_base = Axis::BOTH
.iter() .iter()
.any(|&axis| place[axis].stated_rel_base().is_some()); .any(|&axis| matches!(place[axis].rel_base, RelBase::Len(_)));
if states_rel_base || !self.children.contains(&id.id()) { if states_rel_base || !self.children.contains(&id.id()) {
return self.widget_at(id, place); return self.widget_at(id, place);
} }
@@ -601,16 +608,20 @@ impl Painter<'_> {
for axis in Axis::BOTH { for axis in Axis::BOTH {
let declared = declared[axis]; let declared = declared[axis];
let holds = holds[axis]; let holds = holds[axis];
let at = place[axis];
let result = &mut result[axis]; let result = &mut result[axis];
// Every read became pixels against the window, so a range on // Every read became pixels against the window, so a range on
// it is already in this widget's terms. // it is already in this widget's terms.
result.window = holds.window; result.window = holds.window;
let at = place[axis]; // A length this widget named -- a resolved share, a box a sibling
let reaches = at.stated_rel_base().is_none() // decided, a box it sized outright, which is its own base -- is
&& !at.is_sized() // not a length of this widget's rel base, so a pin on it stops
// here. So does a declaration in pixels: no length of either base
// is in it to see.
let reaches = !matches!(at.rel_base, RelBase::Len(_))
&& declared.is_none_or(|len| len.rel != Rel::ZERO); && declared.is_none_or(|len| len.rel != Rel::ZERO);
result.rel_base = holds.rel_base.and(reaches.then(|| self.rel_base[axis])); result.rel_base = holds.rel_base.and(reaches.then(|| self.rel_base[axis]));
match (at.within_span(), declared.is_some()) { match (at.span, declared) {
// Its box is a part of this widget's own box, in that box's // 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 // own lengths, so what it holds for maps back through that
// part into a range on this widget's box. A length it pinned // part into a range on this widget's box. A length it pinned
@@ -618,7 +629,7 @@ impl Painter<'_> {
// part is the whole of the box less pixels, which is the one // part is the whole of the box less pixels, which is the one
// shape that inverts exactly; any other part pins this // shape that inverts exactly; any other part pins this
// widget's own length. // widget's own length.
(Some(span), false) => { (PlaceSpan::Within(span), None) => {
let part_len = span.len(); let part_len = span.len();
result.region = holds.region.through(part_len); result.region = holds.region.through(part_len);
result.region_len = holds.region_len.map(|pinned| match part_len.rel { result.region_len = holds.region_len.map(|pinned| match part_len.rel {
@@ -641,7 +652,7 @@ impl Painter<'_> {
impl Widgets { impl Widgets {
/// What a widget's box is where a rule or its own hint says so outright. /// What a widget's box is where a rule or its own hint says so outright.
pub(crate) fn declared_lens(&self, id: WidgetId) -> Declared { pub(super) fn declared_lens(&self, id: WidgetId) -> Declared {
let rules = self.size_rules(id); let rules = self.size_rules(id);
let widget = self.get_dyn(id); let widget = self.get_dyn(id);
Declared::from_axes(|axis| { Declared::from_axes(|axis| {
@@ -668,7 +679,7 @@ impl LayoutLen {
/// the rule already gave the region its length, and the rule's length is /// the rule already gave the region its length, and the rule's length is
/// what the widget reports there. And an axis the parent decided from /// what the widget reports there. And an axis the parent decided from
/// the answer is the answer already. /// the answer is the answer already.
pub(crate) fn fills(self, declared: Option<Len>, decided: bool) -> bool { pub(super) fn fills(self, declared: Option<Len>, decided: bool) -> bool {
self.leftover != Weight::ZERO || declared.is_some() || decided self.leftover != Weight::ZERO || declared.is_some() || decided
} }
} }
@@ -683,7 +694,7 @@ impl PlaceDesc {
/// part. That is what makes a fraction the same fraction wherever the part /// part. 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 /// it is placed in sits and however long it is -- the fraction is resolved
/// once, here, against the rel base it was reported of. /// once, here, against the rel base it was reported of.
pub(crate) fn placement( pub(super) fn placement(
self, self,
region: UiRegion, region: UiRegion,
size: Size, size: Size,
@@ -693,7 +704,7 @@ impl PlaceDesc {
let mut placed = region; let mut placed = region;
for axis in Axis::BOTH { for axis in Axis::BOTH {
let reported = size[axis]; let reported = size[axis];
if reported.fills(declared[axis], self[axis].does_fill()) { if reported.fills(declared[axis], self[axis].fills) {
continue; continue;
} }
placed[axis] = placed[axis].place(reported.without_leftover(), align[axis]); placed[axis] = placed[axis].place(reported.without_leftover(), align[axis]);
@@ -711,7 +722,7 @@ impl PlaceDesc {
/// name. The child's declaration is a fraction of whichever reached it, and /// name. The child's declaration is a fraction of whichever reached it, and
/// is the only one that also places the box: a box the caller decided is /// is the only one that also places the box: a box the caller decided is
/// what `place` names. /// what `place` names.
pub(crate) fn rel_base_and_region( pub(super) fn rel_base_and_region(
self, self,
own: UiRegion, own: UiRegion,
parent_rel_base: UiVec2, parent_rel_base: UiVec2,
@@ -722,9 +733,10 @@ impl PlaceDesc {
let mut rel_base = parent_rel_base; let mut rel_base = parent_rel_base;
let mut region = given; let mut region = given;
for axis in Axis::BOTH { for axis in Axis::BOTH {
let base = self[axis] let base = match self[axis].rel_base {
.stated_rel_base() RelBase::Len(len) => len,
.unwrap_or_else(|| parent_rel_base[axis]); RelBase::Inherit | RelBase::WithRegion => parent_rel_base[axis],
};
let len = declared[axis] let len = declared[axis]
.map(|len| len.within_len(base)) .map(|len| len.within_len(base))
.unwrap_or(base); .unwrap_or(base);
+10 -58
View File
@@ -11,21 +11,23 @@ use crate::{Axis, AxisAlign, Len, PrimitiveHandle, RegionAlign, UiRegion, UiSpan
/// it differently, so it is said here. /// it differently, so it is said here.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct PlaceDescAxis { pub struct PlaceDescAxis {
span: PlaceSpan, pub span: PlaceSpan,
fills: bool, pub fills: bool,
rel_base: RelBase, pub rel_base: RelBase,
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
enum PlaceSpan { pub enum PlaceSpan {
Within(UiSpan), Within(UiSpan),
Shifted(UiSpan), Shifted(UiSpan),
Sized(Len), Sized(Len),
} }
/// What a child's fractions are of, where the caller has not named a length. /// What a child's fractions are of. [`PlaceSpan::Sized`] is a length the
/// caller named, which is always its own base, so nothing here constructs one
/// beside anything but [`Self::Len`].
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
enum RelBase { pub enum RelBase {
/// The caller's own, unchanged. /// The caller's own, unchanged.
Inherit, Inherit,
/// The caller's own, narrowed the way the region is. /// The caller's own, narrowed the way the region is.
@@ -61,14 +63,8 @@ impl PlaceDescAxis {
self self
} }
/// Whether the region is the placement outright, rather than a box the
/// answer is placed inside.
pub(crate) const fn does_fill(self) -> bool {
self.fills
}
/// Where it lands in the coordinates `own` is in. /// Where it lands in the coordinates `own` is in.
pub(crate) fn of(self, own: UiSpan, align: AxisAlign) -> UiSpan { pub fn of(self, own: UiSpan, align: AxisAlign) -> UiSpan {
match self.span { match self.span {
PlaceSpan::Within(span) => span.within(&own), PlaceSpan::Within(span) => span.within(&own),
PlaceSpan::Shifted(mut span) => { PlaceSpan::Shifted(mut span) => {
@@ -78,50 +74,6 @@ impl PlaceDescAxis {
PlaceSpan::Sized(len) => own.place(len, align), PlaceSpan::Sized(len) => own.place(len, align),
} }
} }
/// The child's rel base, where this says one outright. `None` forwards
/// the caller's own, and [`RelBase::WithRegion`] is resolved by whoever
/// can read that rel base, so it does not reach here.
pub(crate) const fn stated_rel_base(self) -> Option<Len> {
match self.rel_base {
RelBase::Len(len) => Some(len),
_ => None,
}
}
/// The length this narrows the caller's rel base by, where it does.
/// `None` leaves that rel base alone, and reading it is then a
/// dependency the caller does not take.
pub(crate) const fn narrows_rel_base(self) -> Option<UiSpan> {
match (self.rel_base, self.span) {
(RelBase::WithRegion, PlaceSpan::Within(span)) => Some(span),
_ => None,
}
}
/// The span it composes into the caller's box, where that is what it
/// does: the one case whose validity maps back through the part.
pub(crate) const fn within_span(self) -> Option<UiSpan> {
match self.span {
PlaceSpan::Within(span) => Some(span),
_ => None,
}
}
/// Whether the caller decided this length rather than a place along its
/// own box, which is what stops its length reaching the child at all.
pub(crate) const fn is_sized(self) -> bool {
matches!(self.span, PlaceSpan::Sized(_))
}
/// The same, with its rel base stated outright.
pub(crate) const fn with_rel_base(mut self, len: Option<Len>) -> Self {
self.rel_base = match len {
Some(len) => RelBase::Len(len),
None => RelBase::Inherit,
};
self
}
} }
/// Where a child is asked, on both axes. A [`UiRegion`] converts into the /// Where a child is asked, on both axes. A [`UiRegion`] converts into the
@@ -172,7 +124,7 @@ impl PlaceDesc {
} }
/// The box each axis names, in the coordinates `own` is in. /// The box each axis names, in the coordinates `own` is in.
pub(crate) fn of(self, own: UiRegion, align: RegionAlign) -> UiRegion { pub fn of(self, own: UiRegion, align: RegionAlign) -> UiRegion {
UiRegion::new(self.x.of(own.x, align.x), self.y.of(own.y, align.y)) UiRegion::new(self.x.of(own.x, align.x), self.y.of(own.y, align.y))
} }
} }