One argument says where a child goes and what its fractions are of

`Place` was a product written as a sum -- a `Part` and a fill flag -- and
`Part` named three operations the geometry already had, under words that did
not match them. `Of` was `UiSpan::within`, `From` was `UiSpan::shift`, and
`Sized` was `placement`'s own body with the length given rather than
reported. Both enums are gone.

`PlaceDescAxis` says one axis, named after the operation it performs:
`within`, `shifted`, `sized`, and `WHOLE`. What is optional is a builder --
`fills` and `rel_base` -- so a caller writes only what it decided, and the
rel base it does not write follows the constructor: a span composed into the
caller's box narrows it, a span along a cursor does not, a decided length is
it. That was the one rule a caller could get wrong with nothing failing.

`PlaceDesc` says both axes with named fields, so `axis`, `axis_mut` and
`from_axis` work the way they do on every other pair here, and the joint
work -- resolving a region, reading the fill flags -- is written once rather
than per axis. `widget_at` and `place_at` take `impl Into<PlaceDesc>`, so a
wrapper passes a `UiRegion` and says nothing else. `widget_within` and
`ActiveData::narrow_rel_base` are deleted; `asked` and `placed` carry the
rel base their ask stated.

Cold layout is byte-identical to `84dad21`.
This commit is contained in:
iris-ai committed 2026-09-19 17:59:26 -04:00
1 parent c55be21761
commit 58ce74dd7d
12 files changed
+367 -271

No files matched your search

+8 -13
View File
@@ -1,5 +1,5 @@
use crate::{
LayerId, LayoutHolds, LayoutLen, Len, MaskIdx, MoveIdx, Place, RegionAlign, RetainedPrimitive,
LayerId, LayoutHolds, LayoutLen, MaskIdx, MoveIdx, PlaceDesc, RegionAlign, RetainedPrimitive,
Size, TextureHandle, UiRegion, UiVec2, WidgetId,
};
@@ -14,18 +14,13 @@ pub struct ActiveData {
/// What a fraction declared or reported under this widget is a fraction
/// of, as a length of the window.
pub rel_base: UiVec2,
/// A rel base its parent decided for it on each axis -- a row's slot, or
/// padding's rel base less its pixels -- as a length of the window. `None`
/// forwards the parent's rel base. What it declared is kept separately in
/// `declared` and is a fraction of whichever of the two reached it.
pub narrow_rel_base: [Option<Len>; 2],
/// Where its drawing was put, and where it was asked, each as a part of
/// its parent's box. The two differ where a container asks in one place
/// and puts the answer in another -- a row measures from its cursor and
/// puts the child in its slot. A part is a length from the box's start,
/// so a box that moved re-places every child by re-adding that start.
pub placed: [Place; 2],
pub asked: [Place; 2],
/// Where its drawing was put, and where it was asked. The two differ
/// where a container asks in one place and puts the answer in another --
/// a row measures from its cursor and puts the child in its slot. Each
/// carries the rel base that ask stated, so asking again from either is
/// the same question it was.
pub placed: PlaceDesc,
pub asked: PlaceDesc,
/// The box it was asked in, in the parent's region-node coordinates: the
/// box its drawing was made in and the one its contract is about. Its
/// drawing is placed elsewhere by re-expression, never by asking again.
+57 -100
View File
@@ -1,7 +1,7 @@
#[cfg(feature = "layout-diagnostics")]
use crate::layout_diagnostics::{self as diag, Counter};
use crate::{
Axis, Holds, LayoutHolds, LayoutLen, Len, Part, Place, Px, PxVec2, RegionAlign, Rel,
Axis, Holds, LayoutHolds, LayoutLen, Len, PlaceDesc, Px, PxVec2, RegionAlign, Rel,
RenderedText, RetainedPrimitive, Size, StrongWidget, TextAttrs, TextBuffer, TextData,
TextureHandle, UiRegion, UiRenderState, UiRsc, UiSpan, UiVec2, Weight, WidgetId, Widgets,
render::{
@@ -144,68 +144,49 @@ impl<'a> Painter<'a> {
};
}
/// Draws a widget in the whole of this widget's own box, with the rel base
/// forwarded unchanged: what a container that is only a wrapper around
/// one child wants, and what every transparent container passes for the
/// rel base.
/// Draws a widget in the whole of this widget's own box, with the rel
/// base forwarded unchanged: what a container that is only a wrapper
/// around one child wants.
pub fn widget<'s, W: ?Sized>(&'s mut self, id: &'s StrongWidget<W>) -> DrawResult<'s, 'a, W> {
self.widget_within(id, UiRegion::FULL)
self.widget_at(id, UiRegion::FULL)
}
/// Draws a widget in `region` of this widget's own box, in that box's own
/// coordinates -- an inset, or an offset.
///
/// The child's rel base is this widget's narrowed the same way the box is,
/// so padding takes its pixels off both and an offset, which changes the
/// box's length by nothing, changes neither. An axis the region leaves
/// whole is not read at all, so a wrapper that only moves its child does
/// not pin the drawing to a rel base.
pub fn widget_within<'s, W: ?Sized>(
&'s mut self,
id: &'s StrongWidget<W>,
region: UiRegion,
) -> DrawResult<'s, 'a, W> {
let narrow = AXES.map(|axis| {
let len = region.axis(axis).len();
(len != Len::FULL).then(|| len.within_len(self.rel_base(axis)))
});
self.widget_at(id, region_places(region), narrow)
/// Resolves what the place says about the child's rel base into a length,
/// where that is this widget's own narrowed the way the region is. An
/// axis the region leaves whole is not read at all, so a wrapper that
/// only moves its child does not pin its drawing to a rel base.
fn state_rel_base(&mut self, mut place: PlaceDesc) -> PlaceDesc {
for axis in AXES {
if let Some(span) = place.axis(axis).narrows_rel_base() {
let len = span.len();
let stated = (len != Len::FULL).then(|| len.within_len(self.rel_base(axis)));
*place.axis_mut(axis) = place.axis(axis).with_rel_base(stated);
}
}
place
}
/// Asks a child, saying what its fractions are of and where it is asked.
///
/// `place` is the child's region, per axis, said as a part of this
/// widget's own: see [`Place`]. The child draws once, in that region,
/// and its answer is placed inside it by re-expressing the drawing.
/// Nothing is drawn again in a box an answer chose; a container that
/// puts the answer somewhere else says so with [`Self::place_at`].
/// `place` says where the child goes and what its fractions are of:
/// see [`PlaceDesc`]. A `UiRegion` converts into the common case, which
/// is a box of this widget's own with the answer placed inside it.
///
/// `narrow_rel_base` is the child's rel base, per axis, as a length of
/// the window: a resolved share, or a box a sibling's answer decided.
/// `None`, whole or per axis, forwards this widget's own -- which is
/// what a container that only divides room passes, so a fraction under
/// it means the same wherever it sits and however deeply it is nested.
/// It only ever narrows: a length the child declares narrows it again
/// here whatever the caller says, and what comes of it also narrows the
/// region, placed in the part by the child's alignment.
/// The child draws once, in the region that comes of it, and its answer
/// is placed inside that region by re-expressing the drawing. Nothing is
/// drawn again in a box an answer chose; a container that puts the
/// answer somewhere else says so with [`Self::place_at`].
pub fn widget_at<'s, W: ?Sized>(
&'s mut self,
id: &'s StrongWidget<W>,
place: [Place; 2],
narrow_rel_base: impl Into<Option<[Option<Len>; 2]>>,
place: impl Into<PlaceDesc>,
) -> DrawResult<'s, 'a, W> {
let narrow_rel_base = narrow_rel_base.into().unwrap_or([None; 2]);
let place = self.state_rel_base(place.into());
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 (rel_base, region) = rel_base_and_region(
self.region,
self.rel_base,
place,
narrow_rel_base,
declared,
align,
);
let (rel_base, region) =
rel_base_and_region(self.region, self.rel_base, place, declared, align);
#[cfg(feature = "layout-diagnostics")]
if region_node {
diag::bump(Counter::RegionNodeDraws);
@@ -230,15 +211,14 @@ impl<'a> Painter<'a> {
region,
placed: place,
asked: place,
narrow_rel_base,
re_asked,
px,
},
None,
self.rsc,
);
let holds = self.in_parent(holds, region, place, narrow_rel_base, declared);
let answer_holds = self.in_parent(answer_holds, region, place, narrow_rel_base, declared);
let holds = self.in_parent(holds, region, place, declared);
let answer_holds = self.in_parent(answer_holds, region, place, declared);
match self.under.iter_mut().find(|(child, _)| *child == id.id()) {
Some((_, kept)) => *kept = holds,
None => self.under.push((id.id(), holds)),
@@ -273,12 +253,14 @@ impl<'a> Painter<'a> {
pub fn place_at<'s, W: ?Sized>(
&'s mut self,
id: &'s StrongWidget<W>,
place: [Place; 2],
narrow_rel_base: impl Into<Option<[Option<Len>; 2]>>,
place: impl Into<PlaceDesc>,
) -> DrawResult<'s, 'a, W> {
let narrow_rel_base = narrow_rel_base.into().unwrap_or([None; 2]);
if narrow_rel_base.iter().any(Option::is_some) || !self.children.contains(&id.id()) {
return self.widget_at(id, place, narrow_rel_base);
let place = self.state_rel_base(place.into());
let states_rel_base = AXES
.iter()
.any(|&axis| place.axis(axis).stated_rel_base().is_some());
if states_rel_base || !self.children.contains(&id.id()) {
return self.widget_at(id, place);
}
let at = self.placing();
self.state.place_in(id.id(), &at, place, self.rsc);
@@ -619,8 +601,7 @@ impl Painter<'_> {
&self,
holds: LayoutHolds,
region: UiRegion,
place: [Place; 2],
narrow_rel_base: [Option<Len>; 2],
place: PlaceDesc,
declared: [Option<LayoutLen>; 2],
) -> LayoutHolds {
let mut result = LayoutHolds::ANY;
@@ -629,11 +610,12 @@ impl Painter<'_> {
// Every read became pixels against the window, so a range on
// it is already in this widget's terms.
result.window[n] = holds.window[n];
let reaches = narrow_rel_base[n].is_none()
&& !matches!(place[n].part(), Part::Sized(_))
let at = *place.axis(axis);
let reaches = at.stated_rel_base().is_none()
&& !at.is_sized()
&& declared[n].is_none_or(|len| len.rel != Rel::ZERO);
result.rel_base[n] = holds.rel_base[n].and(reaches.then(|| self.rel_base.axis(axis)));
match (place[n].part(), declared[n].is_some()) {
match (at.within_span(), declared[n].is_some()) {
// 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
// part into a range on this widget's box. A length it pinned
@@ -641,7 +623,7 @@ impl Painter<'_> {
// part is the whole of the box less pixels, which is the one
// shape that inverts exactly; any other part pins this
// widget's own length.
(Part::Of(span), false) => {
(Some(span), false) => {
let part_len = span.len();
result.region[n] = holds.region[n].through(part_len);
result.region_len[n] = holds.region_len[n].map(|pinned| match part_len.rel {
@@ -663,14 +645,6 @@ impl Painter<'_> {
}
}
/// A box of a widget's own, per axis, with the answer placed inside it.
fn region_places(region: UiRegion) -> [Place; 2] {
[
Place::Within(Part::Of(region.x)),
Place::Within(Part::Of(region.y)),
]
}
/// What a widget declares a length of its box to be. `leftover` is not one: a
/// share of what is left over is only a length to the widget dividing one,
/// so it passes up in the size instead.
@@ -715,14 +689,14 @@ pub(crate) fn placement(
region: UiRegion,
size: Size,
declared: [Option<LayoutLen>; 2],
fill: [bool; 2],
place: PlaceDesc,
align: RegionAlign,
) -> UiRegion {
let mut placed = region;
for axis in AXES {
let n = axis as usize;
let reported = size.axis(axis);
if fills(reported, declared[n], fill[n]) {
if fills(reported, declared[n], place.axis(axis).does_fill()) {
continue;
}
let len = Len::from_parts(reported.rel, reported.px);
@@ -737,32 +711,27 @@ pub(crate) fn placement(
/// widget asking draws in.
///
/// `own` is that widget's own box, and `place` what of it the child is
/// given. `narrow_rel_base` is a rel base the container decided for the child -- a row's
/// slot, or padding's rel base less its pixels -- and [`Part::Sized`] one a
/// sibling's answer decided; both are window lengths, like every other
/// length here, since a slot of a row is not a fraction of anything the row
/// 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.
/// given, including any rel base it states -- a row's slot, or padding's rel
/// base less its pixels. That is a window length, like every other length
/// here, since a slot of a row is not a fraction of anything the row can
/// 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
/// what `place` names.
pub(crate) fn rel_base_and_region(
own: UiRegion,
parent_rel_base: UiVec2,
place: [Place; 2],
narrow_rel_base: [Option<Len>; 2],
place: PlaceDesc,
declared: [Option<LayoutLen>; 2],
align: RegionAlign,
) -> (UiVec2, UiRegion) {
let given = region_of(own, place, align);
let given = place.of(own, align);
let mut rel_base = parent_rel_base;
let mut region = given;
for axis in AXES {
let n = axis as usize;
let sized = match place[n].part() {
Part::Sized(len) => Some(len),
_ => None,
};
let base = sized
.or(narrow_rel_base[n])
let base = place
.axis(axis)
.stated_rel_base()
.unwrap_or_else(|| parent_rel_base.axis(axis));
let len = declared[n]
.map(|len| Len::from_parts(len.rel, len.px).within_len(base))
@@ -776,15 +745,3 @@ pub(crate) fn rel_base_and_region(
}
(rel_base, region)
}
/// The part of a widget's own box a `place` names, in the coordinates that
/// box is in.
fn region_of(own: UiRegion, place: [Place; 2], align: RegionAlign) -> UiRegion {
let mut region = own;
for axis in AXES {
*region.axis_mut(axis) = place[axis as usize]
.part()
.of(*own.axis(axis), align.axis(axis));
}
region
}
+222 -50
View File
@@ -1,70 +1,242 @@
use crate::{AxisAlign, Len, PrimitiveHandle, UiRegion, UiSpan};
use crate::{Axis, AxisAlign, Len, PrimitiveHandle, RegionAlign, UiRegion, UiSpan};
/// A child's region along one axis, said as a part of the region the widget
/// saying it was given.
/// How a child's region along one axis comes from the region of the widget
/// asking, and what its fractions are of.
///
/// The three ways of saying a region are the three the geometry already has:
/// a span composed into the caller's box, a span shifted to where that box
/// starts, and a length placed in it by alignment. Which one is meant cannot
/// be read off the numbers, since two of them take the same span and apply
/// it differently, so it is said here.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Part {
/// Window lengths from where the box starts, which is what a container
/// dividing room among its children speaks: a child's report is a window
/// length, so the cursor that sums those reports is one too. A moved box
/// re-places every child by re-adding its start, exactly. A fraction
/// here is a fraction of the window and not of the box -- the whole of a
/// box is [`Self::WHOLE`], not a `rel(1.0)` span.
From(UiSpan),
/// A part of the box in its own coordinates, which is what a container
/// that insets one speaks: taking eleven pixels off the end needs no
/// length, where saying the same thing in window lengths would make the
/// container read its own box -- and a box chosen from its own answer
/// then feeds back into the answer.
Of(UiSpan),
/// A box of this length, wherever in the parent's box the child's own
/// alignment puts it, and that same length as its rel base. Unlike `From`,
/// it is a length decided from above rather than a place along a
/// container's cursor -- what a stack's sizing child decides for the
/// rest.
pub struct PlaceDescAxis {
span: PlaceSpan,
fills: bool,
rel_base: RelBase,
}
#[derive(Clone, Copy, Debug, PartialEq)]
enum PlaceSpan {
Within(UiSpan),
Shifted(UiSpan),
Sized(Len),
}
impl Part {
/// The whole of the box. Not a variant of its own: it composes and
/// inverts through the same expressions every other `Of` does.
pub const WHOLE: Self = Self::Of(UiSpan::FULL);
/// What a child's fractions are of, where the caller has not named a length.
#[derive(Clone, Copy, Debug, PartialEq)]
enum RelBase {
/// The caller's own, unchanged.
Inherit,
/// The caller's own, narrowed the way the region is.
WithRegion,
/// This length of the window.
Len(Len),
}
impl PlaceDescAxis {
/// The whole of the caller's box.
pub const WHOLE: Self = Self::within(UiSpan::FULL);
/// `span` composed into the caller's own box, so it moves and scales
/// with it: [`UiSpan::within`], which is what a container that insets
/// one speaks. Taking eleven pixels off the end needs no length, where
/// saying the same thing in window lengths would make the container read
/// its own box -- and a box chosen from its own answer then feeds back
/// into the answer.
///
/// The child's rel base is narrowed the same way, so padding takes its
/// pixels off both and `rel(1)` under it fills the caller rather than
/// overflowing it.
pub const fn within(span: UiSpan) -> Self {
Self {
span: PlaceSpan::Within(span),
fills: false,
rel_base: RelBase::WithRegion,
}
}
/// `span` shifted to where the caller's own box starts: window lengths
/// along a cursor, which is what a container dividing room among its
/// children speaks. A child's report is a window length, so the cursor
/// that sums those reports is one too, and a moved box re-places every
/// child by re-adding its start, exactly.
///
/// The child's rel base passes through: how far along the cursor a child
/// sits says nothing about what a fraction under it is of.
pub const fn shifted(span: UiSpan) -> Self {
Self {
span: PlaceSpan::Shifted(span),
fills: false,
rel_base: RelBase::Inherit,
}
}
/// A box this long, placed in the caller's own by the child's alignment:
/// the rule that places an answer, with the length given from above
/// rather than reported. What a stack's sizing child decides for the
/// rest. It is the child's rel base too.
pub const fn sized(len: Len) -> Self {
Self {
span: PlaceSpan::Sized(len),
fills: false,
rel_base: RelBase::Len(len),
}
}
/// This region is the child's placement: its answer is not placed inside
/// it again. A container uses it where it hands back exactly what the
/// child asked for -- a row placing a child at the length it reported.
pub const fn fills(mut self) -> Self {
self.fills = true;
self
}
/// What the child's fractions are of, as a length of the window: a
/// resolved share, or a box a sibling's answer decided.
pub const fn rel_base(mut self, len: Len) -> Self {
self.rel_base = RelBase::Len(len);
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.
pub(crate) fn of(self, own: UiSpan, align: AxisAlign) -> UiSpan {
match self {
Self::From(span) => UiSpan::new(own.start + span.start, own.start + span.end),
Self::Of(span) => span.within(&own),
Self::Sized(len) => {
match self.span {
PlaceSpan::Within(span) => span.within(&own),
PlaceSpan::Shifted(mut span) => {
span.shift(own.start);
span
}
PlaceSpan::Sized(len) => {
let start = own.start + (own.len() - len).scale(align.rel());
UiSpan::new(start, start + len)
}
}
}
}
/// A child's region along one axis, and what becomes of its placement in
/// that region once it has answered.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Place {
/// The placement is the child's answer, aligned inside the region by the
/// child's alignment.
Within(Part),
/// The region is the placement; the answer is not placed inside it again.
Fill(Part),
}
impl Place {
pub(crate) fn part(self) -> Part {
match self {
Self::Within(part) | Self::Fill(part) => part,
/// 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,
}
}
/// Whether the region is the placement outright, rather than a box the
/// answer is placed inside.
pub(crate) fn fills(self) -> bool {
matches!(self, Self::Fill(_))
/// 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
/// common case: that box of the caller's own, the answer placed inside it.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PlaceDesc {
pub x: PlaceDescAxis,
pub y: PlaceDescAxis,
}
impl PlaceDesc {
/// The whole of the caller's box, on both axes.
pub const WHOLE: Self = Self::splat(PlaceDescAxis::WHOLE);
pub const fn new(x: PlaceDescAxis, y: PlaceDescAxis) -> Self {
Self { x, y }
}
/// The same on both axes.
pub const fn splat(place: PlaceDescAxis) -> Self {
Self { x: place, y: place }
}
/// `aligned` on `axis` and `ortho` on the other, which is how a
/// container that divides one axis says what it is doing.
pub fn from_axis(axis: Axis, aligned: PlaceDescAxis, ortho: PlaceDescAxis) -> Self {
match axis {
Axis::X => Self::new(aligned, ortho),
Axis::Y => Self::new(ortho, aligned),
}
}
pub const fn axis(&self, axis: Axis) -> &PlaceDescAxis {
match axis {
Axis::X => &self.x,
Axis::Y => &self.y,
}
}
pub const fn axis_mut(&mut self, axis: Axis) -> &mut PlaceDescAxis {
match axis {
Axis::X => &mut self.x,
Axis::Y => &mut self.y,
}
}
/// Both regions are the child's placement. See [`PlaceDescAxis::fills`].
pub const fn fills(self) -> Self {
Self::new(self.x.fills(), self.y.fills())
}
/// The child's rel base on one axis. See [`PlaceDescAxis::rel_base`].
pub const fn rel_base(mut self, axis: Axis, len: Len) -> Self {
*self.axis_mut(axis) = self.axis(axis).rel_base(len);
self
}
/// The box each axis names, in the coordinates `own` is in.
pub(crate) fn of(self, own: UiRegion, align: RegionAlign) -> UiRegion {
UiRegion::new(self.x.of(own.x, align.x), self.y.of(own.y, align.y))
}
}
impl From<UiRegion> for PlaceDesc {
fn from(region: UiRegion) -> Self {
Self::new(
PlaceDescAxis::within(region.x),
PlaceDescAxis::within(region.y),
)
}
}
impl From<PlaceDescAxis> for PlaceDesc {
fn from(place: PlaceDescAxis) -> Self {
Self::splat(place)
}
}
+18 -38
View File
@@ -3,7 +3,7 @@ use crate::layout_diagnostics::{self as diag, Counter, ReuseOutcome, TimerKind};
use crate::ui::painter::{declared_lens, placement, rel_base_and_region};
use crate::{
ActiveData, Axis, DrawLayers, IdLike, LayoutHolds, LayoutLen, Len, MaskIdx, MoveIdx, Moves,
Painter, Part, PixelRegion, Place, PxVec2, Rel, Size, StrongWidget, UiRegion, UiRsc, UiSpan,
Painter, PixelRegion, PlaceDesc, PxVec2, Rel, Size, StrongWidget, UiRegion, UiRsc, UiSpan,
UiVec2, Weight, WidgetId, Widgets,
util::{HashMap, Vec2},
};
@@ -27,27 +27,16 @@ pub(super) struct DrawInfo {
/// coordinates.
pub region: UiRegion,
/// Where the widget is put, and where it was asked, as parts of the
/// parent's box. See [`Place`]. The two are one ask's place until the
/// parent's box. See [`PlaceDesc`]. The two are one ask's place until the
/// parent puts the answer somewhere else.
pub placed: [Place; 2],
pub asked: [Place; 2],
/// A rel base the parent decided for it on each axis, as a length of the
/// window, which the widget's own declaration is a fraction of.
pub narrow_rel_base: [Option<Len>; 2],
pub placed: PlaceDesc,
pub asked: PlaceDesc,
/// Whether the parent already asked about this widget in this draw.
pub re_asked: bool,
/// The rel base in pixels, resolved once against the window.
pub px: PxVec2,
}
impl DrawInfo {
/// The axes where the part is the drawing's box outright, which are the
/// axes the answer is not placed inside it again.
fn fill(&self) -> [bool; 2] {
self.placed.map(Place::fills)
}
}
/// What a widget's children are placed in: its own box, the coordinates its
/// drawing is in, and what else one ask of a child is decided from.
pub(super) struct Placing {
@@ -133,7 +122,7 @@ impl UiRenderState {
}
}
/// The root is asked about in the output. Its own rules narrow_rel_base both its
/// The root is asked about in the output. Its own rules narrow both its
/// rel base and box; nothing above it chose a different one.
fn root_info(&self, rel_base: UiVec2, region: UiRegion) -> DrawInfo {
let px = rel_base.to_px(self.output_size);
@@ -146,9 +135,8 @@ impl UiRenderState {
mask: MaskIdx::NONE,
rel_base,
region,
placed: [Place::Within(Part::WHOLE); 2],
asked: [Place::Within(Part::WHOLE); 2],
narrow_rel_base: [None; 2],
placed: PlaceDesc::WHOLE,
asked: PlaceDesc::WHOLE,
re_asked: false,
px,
}
@@ -209,8 +197,7 @@ impl UiRenderState {
rel_base_and_region(
UiRegion::FULL,
UiVec2::FULL_SIZE,
[Place::Within(Part::WHOLE); 2],
[None; 2],
PlaceDesc::WHOLE,
declared_lens(widgets, id),
widgets.alignment(id),
)
@@ -248,7 +235,7 @@ impl UiRenderState {
.then(|| self.retained_answer(id, region, info))
.flatten()
.and_then(|answer| {
let placed = placement(region, answer.0, declared, info.fill(), align);
let placed = placement(region, answer.0, declared, info.placed, align);
self.try_reuse(id, region, placed, info, rsc)
.map(|()| answer)
});
@@ -260,7 +247,7 @@ impl UiRenderState {
// Where the drawing goes: the part its parent gave it, with the
// answer placed inside that part on any axis the parent left
// open.
let placed = placement(region, answer.0, declared, info.fill(), align);
let placed = placement(region, answer.0, declared, info.placed, align);
if placed != region {
self.relocate(id, placed, info, rsc);
}
@@ -273,7 +260,6 @@ impl UiRenderState {
// and what of its own box it asked in. A local redraw asks the same
// question again from these.
active.rel_base = info.rel_base;
active.narrow_rel_base = info.narrow_rel_base;
active.re_asked = info.re_asked;
active.answer = Some(answer);
active.asked = info.asked;
@@ -468,9 +454,8 @@ impl UiRenderState {
mask,
rel_base: UiVec2::FULL_SIZE,
region: UiRegion::FULL,
placed: [Place::Within(Part::WHOLE); 2],
asked: [Place::Within(Part::WHOLE); 2],
narrow_rel_base: [None; 2],
placed: PlaceDesc::WHOLE,
asked: PlaceDesc::WHOLE,
re_asked: false,
px,
},
@@ -484,7 +469,6 @@ impl UiRenderState {
id,
placement: region,
rel_base: info.rel_base,
narrow_rel_base: info.narrow_rel_base,
placed: info.placed,
asked: info.asked,
region,
@@ -718,7 +702,7 @@ impl UiRenderState {
&mut self,
child: WidgetId,
at: &Placing,
place: [Place; 2],
place: PlaceDesc,
rsc: &mut dyn UiRsc,
) {
let active = &self.active[&child];
@@ -727,7 +711,7 @@ impl UiRenderState {
region,
active.measured().unwrap_or(active.size),
active.declared,
place.map(Place::fills),
place,
active.own_align,
);
let info = DrawInfo {
@@ -741,7 +725,6 @@ impl UiRenderState {
region,
placed: place,
asked: active.asked,
narrow_rel_base: active.narrow_rel_base,
re_asked: active.re_asked,
px: rel_base.to_px(at.window),
};
@@ -752,12 +735,11 @@ impl UiRenderState {
/// the box its parent is being taken as. What narrowed its rel base and what
/// it declared are its own record's, so both are resolved against that
/// parent's rel base again exactly as the first ask resolved them.
fn ask_again(active: &ActiveData, at: &Placing, place: [Place; 2]) -> (UiVec2, UiRegion) {
fn ask_again(active: &ActiveData, at: &Placing, place: PlaceDesc) -> (UiVec2, UiRegion) {
rel_base_and_region(
at.region,
at.rel_base,
place,
active.narrow_rel_base,
active.declared,
active.own_align,
)
@@ -886,9 +868,8 @@ impl UiRenderState {
id,
placement: UiRegion::FULL,
rel_base: UiVec2::FULL_SIZE,
narrow_rel_base: [None; 2],
placed: [Place::Within(Part::WHOLE); 2],
asked: [Place::Within(Part::WHOLE); 2],
placed: PlaceDesc::WHOLE,
asked: PlaceDesc::WHOLE,
region: UiRegion::FULL,
answer: None,
re_asked: false,
@@ -1146,7 +1127,6 @@ impl UiRenderState {
region,
placed: active.asked,
asked: active.asked,
narrow_rel_base: active.narrow_rel_base,
re_asked: false,
px: rel_base.to_px(self.output_size),
};
@@ -1171,7 +1151,7 @@ impl UiRenderState {
}
if active.answer != was_answer || active.holds != was_holds {
// The parent retains both the answer and the drawing's validity;
// even an unchanged size can narrow_rel_base the range safe for a resize.
// even an unchanged size can narrow the range safe for a resize.
#[cfg(feature = "layout-diagnostics")]
{
diag::bump(Counter::SizeChanges);