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:
1 parent
c55be21761
commit
58ce74dd7d
12 files changed
+367
-271
No files matched your search
+57
-100
@@ -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
|
||||
}
|
||||
Reference in new issue
Block a user