Ask each child once and place its answer by re-expression
A widget draws in the box it is asked in and its answer is placed inside that box by re-expressing the drawing; nothing is drawn again in a box an answer chose. The offer machinery, whose job was to tell a measuring draw from a placing one, goes with the placing draw. A span measures each child from its cursor and moves fixed children to their slots with place_at; a share child is asked once more in its decided slot with its frame narrowed to it. A stack asks non-sizing children in the box its sizing child decided. A scroll asks its content once and moves it to the scrolled offset. A local redraw asks the retained question again and puts the answer back where the parent placed it. A symbolic length a child pinned composes through Part::Of exactly where the part is the whole box less pixels, and pins the parent's own length otherwise; dropping it let a pad reuse a drawing across a narrowed frame of the same pixel length (shrinker seeds 60, 248 and 384 at depth 5). Suite 114/114 including the two decided-box pins, fast oracle 11/11, shrinker 400 seeds at depth 5 over all fifteen cases. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
4328eac756
commit
3091fb86df
12 files changed
+353
-354
No files matched your search
+101
-94
@@ -1,14 +1,14 @@
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
use crate::layout_diagnostics::{self as diag, Counter};
|
||||
use crate::{
|
||||
Axis, Holds, LayoutHolds, LayoutLen, Len, Part, Place, Px, PxVec2, RegionAlign, RenderedText,
|
||||
RetainedPrimitive, Size, StrongWidget, TextAttrs, TextBuffer, TextData, TextureHandle,
|
||||
UiRegion, UiRenderState, UiRsc, UiSpan, UiVec2, Weight, WidgetId, Widgets,
|
||||
Axis, Holds, LayoutHolds, LayoutLen, Len, Part, Place, Px, PxVec2, RegionAlign, Rel,
|
||||
RenderedText, RetainedPrimitive, Size, StrongWidget, TextAttrs, TextBuffer, TextData,
|
||||
TextureHandle, UiRegion, UiRenderState, UiRsc, UiSpan, UiVec2, Weight, WidgetId, Widgets,
|
||||
render::{
|
||||
GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveInst, PrimitiveKind,
|
||||
TexturePrimitive,
|
||||
},
|
||||
ui::render_state::DrawInfo,
|
||||
ui::render_state::{DrawInfo, Placing},
|
||||
};
|
||||
const AXES: [Axis; 2] = [Axis::X, Axis::Y];
|
||||
|
||||
@@ -44,13 +44,6 @@ pub struct Painter<'a> {
|
||||
/// Only children whose answers were read constrain this widget's answer.
|
||||
pub(super) answer_under: LayoutHolds,
|
||||
pub(super) children: Vec<WidgetId>,
|
||||
/// The children asked about so far, so the first place each was asked in
|
||||
/// is the one recorded as its offer.
|
||||
pub(super) offered: Vec<WidgetId>,
|
||||
/// Whether this draw is at the place its parent first asked about, which
|
||||
/// makes the questions it asks the ones a cold layout asks and their
|
||||
/// answers the ones to keep.
|
||||
pub(super) at_offer: bool,
|
||||
/// The children whose size this widget read while drawing.
|
||||
pub(super) size_deps: Vec<WidgetId>,
|
||||
/// What this draw itself read of its frame in pixels, per axis: every
|
||||
@@ -58,8 +51,9 @@ pub struct Painter<'a> {
|
||||
pub(super) frame_own: [Holds; 2],
|
||||
/// The same for its own box.
|
||||
pub(super) extent_own: [Holds; 2],
|
||||
/// Dependencies of every child drawing, including unmeasured overlays.
|
||||
pub(super) under: 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)>,
|
||||
/// The movable region this widget's primitives are positioned through:
|
||||
/// its own when opted in, otherwise the nearest ancestor's.
|
||||
pub(super) move_idx: MoveIdx,
|
||||
@@ -151,37 +145,40 @@ impl<'a> Painter<'a> {
|
||||
/// one child wants, and what every transparent container passes for the
|
||||
/// frame.
|
||||
pub fn widget<'s, W: ?Sized>(&'s mut self, id: &'s StrongWidget<W>) -> DrawResult<'s, 'a, W> {
|
||||
self.widget_at(id, UiRegion::FULL, [Place::Within(Part::All); 2])
|
||||
self.widget_at(id, [None; 2], [Place::Within(Part::All); 2])
|
||||
}
|
||||
|
||||
/// Draws a child, saying what its fractions are of and where its drawing
|
||||
/// goes.
|
||||
/// Asks a child, saying what its fractions are of and where it is asked.
|
||||
///
|
||||
/// `frame` is that reference, in this widget's own frame coordinates:
|
||||
/// [`UiRegion::FULL`] forwards this widget's frame, 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. Narrowing
|
||||
/// it is for what is decided from above, and a declared length narrows
|
||||
/// it here.
|
||||
/// `narrow` is a length this widget decided for the child's frame, per
|
||||
/// axis, as a length of this widget's own frame: a resolved share, or a
|
||||
/// box a sibling's answer decided. `None` forwards this widget's frame,
|
||||
/// 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. A declared length narrows the frame here whatever the
|
||||
/// caller says. A narrowed frame is placed in the part by the child's
|
||||
/// alignment and is the box the child is asked in.
|
||||
///
|
||||
/// `place` is where the drawing goes, per axis, as a part of this
|
||||
/// widget's extent: see [`Place`]. A narrowed frame is its own extent,
|
||||
/// since the narrowing is what said where the drawing goes.
|
||||
/// `place` is where the child is asked, per axis, as a part of this
|
||||
/// widget's box: see [`Place`]. The child draws once, in that box, 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`].
|
||||
pub fn widget_at<'s, W: ?Sized>(
|
||||
&'s mut self,
|
||||
id: &'s StrongWidget<W>,
|
||||
frame: UiRegion,
|
||||
narrow: [Option<Len>; 2],
|
||||
place: [Place; 2],
|
||||
) -> DrawResult<'s, 'a, W> {
|
||||
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 (local, extent) = frame_and_extent(
|
||||
frame,
|
||||
part_of(self.extent, place),
|
||||
narrowed_by(declared, frame),
|
||||
align,
|
||||
);
|
||||
let declared_narrow = narrowed_by(declared);
|
||||
let narrow = [
|
||||
declared_narrow[0].or(narrow[0]),
|
||||
declared_narrow[1].or(narrow[1]),
|
||||
];
|
||||
let (local, extent) = frame_and_extent(part_of(self.extent, place), narrow, align);
|
||||
let within = match local == UiRegion::FULL {
|
||||
true => self.frame,
|
||||
false => local.within(&self.frame),
|
||||
@@ -195,19 +192,7 @@ impl<'a> Painter<'a> {
|
||||
if !self.children.contains(&id.id()) {
|
||||
self.children.push(id.id());
|
||||
}
|
||||
let first_ask = self.offer(id.id());
|
||||
let offer_place = if first_ask {
|
||||
place
|
||||
} else {
|
||||
self.state
|
||||
.active
|
||||
.get(&id.id())
|
||||
.map_or(place, |a| a.offer_place)
|
||||
};
|
||||
let px = local.size().to_px(self.px);
|
||||
// The answer and what it holds for, both about the place asked in.
|
||||
// The child's record may say something else once its drawing has been
|
||||
// placed: a drawing made again in its placed box holds for that box.
|
||||
let (size, answer_holds, holds) = self.state.draw_inner(
|
||||
id.id(),
|
||||
DrawInfo {
|
||||
@@ -221,14 +206,20 @@ impl<'a> Painter<'a> {
|
||||
frame_abs: within,
|
||||
part: extent,
|
||||
place,
|
||||
offer_place,
|
||||
offer_place: place,
|
||||
narrow,
|
||||
px,
|
||||
},
|
||||
None,
|
||||
self.rsc,
|
||||
);
|
||||
let compose = |holds| in_parent(holds, local, extent, place, declared);
|
||||
self.under = self.under.and(compose(holds));
|
||||
let own = self.extent;
|
||||
let compose = |holds| in_parent(holds, local, extent, place, narrow, own);
|
||||
let holds = compose(holds);
|
||||
match self.under.iter_mut().find(|(child, _)| *child == id.id()) {
|
||||
Some((_, kept)) => *kept = holds,
|
||||
None => self.under.push((id.id(), holds)),
|
||||
}
|
||||
DrawResult {
|
||||
child: id,
|
||||
painter: self,
|
||||
@@ -242,9 +233,37 @@ impl<'a> Painter<'a> {
|
||||
/// this frame; what it answered is still something this widget asked.
|
||||
pub fn undraw<W: ?Sized>(&mut self, id: &StrongWidget<W>) {
|
||||
self.children.retain(|child| *child != id.id());
|
||||
self.under.retain(|(child, _)| *child != id.id());
|
||||
self.state.undraw_rec(id.id(), self.rsc);
|
||||
}
|
||||
|
||||
/// Puts a child asked about in this draw somewhere else in this
|
||||
/// widget's box: its answer, placed in this part instead. The drawing
|
||||
/// is re-expressed there rather than made again -- what a row does once
|
||||
/// it knows every slot, having measured each child from its cursor.
|
||||
pub fn place_at<W: ?Sized>(&mut self, id: &StrongWidget<W>, place: [Place; 2]) {
|
||||
debug_assert!(
|
||||
self.children.contains(&id.id()),
|
||||
"'{}' placed a child it did not ask about in this draw",
|
||||
self.label()
|
||||
);
|
||||
let at = self.placing();
|
||||
self.state.place_in(id.id(), &at, place, self.rsc);
|
||||
}
|
||||
|
||||
/// This widget as the thing its children are placed within.
|
||||
fn placing(&self) -> Placing {
|
||||
Placing {
|
||||
id: self.id,
|
||||
extent: self.extent,
|
||||
local: self.frame,
|
||||
px: self.px,
|
||||
depth: self.depth,
|
||||
move_idx: self.move_idx,
|
||||
mask: self.mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// What a widget's rules declare its lengths to be, which whoever draws
|
||||
/// it resolves into its frame. Reading them depends on nothing -- the box
|
||||
/// that comes of them is kept on the child, and `redraw` compares it
|
||||
@@ -281,19 +300,6 @@ impl<'a> Painter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this is the first box a child is asked about in during a draw
|
||||
/// that is itself the one its parent measured -- the question a cold
|
||||
/// layout asks, whose answer is the one to keep. A drawing made again in
|
||||
/// a box chosen from an answer asks about that box instead, and what it
|
||||
/// hears back is not a measurement of anything.
|
||||
fn offer(&mut self, child: WidgetId) -> bool {
|
||||
if !self.at_offer || self.offered.contains(&child) {
|
||||
return false;
|
||||
}
|
||||
self.offered.push(child);
|
||||
true
|
||||
}
|
||||
|
||||
fn depend_on<W: ?Sized>(&mut self, child: &StrongWidget<W>) {
|
||||
if !self.size_deps.contains(&child.id()) {
|
||||
self.size_deps.push(child.id());
|
||||
@@ -537,19 +543,21 @@ impl PrimitiveLike for &TextureHandle {
|
||||
/// reach it as one length, so what it holds for maps back through that
|
||||
/// length exactly -- and where the box it was given is this widget's own,
|
||||
/// what it says about that box is what this widget can say about its own.
|
||||
/// `own` is this widget's own box, for a pin that cannot be said exactly.
|
||||
pub(crate) fn in_parent(
|
||||
holds: LayoutHolds,
|
||||
frame: UiRegion,
|
||||
extent: UiRegion,
|
||||
place: [Place; 2],
|
||||
declared: [Option<LayoutLen>; 2],
|
||||
narrow: [Option<Len>; 2],
|
||||
own: UiRegion,
|
||||
) -> LayoutHolds {
|
||||
let mut result = LayoutHolds::ANY;
|
||||
for axis in AXES {
|
||||
let n = axis as usize;
|
||||
let frame_len = frame.axis(axis).len();
|
||||
result.frame[n] = holds.frame[n].through(frame_len);
|
||||
match (place[n].part(), declared[n]) {
|
||||
match (place[n].part(), narrow[n]) {
|
||||
// Its box is this widget's own, or a part of it in that box's
|
||||
// own lengths: so what it holds for is a range on this widget's
|
||||
// own box, which is what lets that box move without a redraw. A
|
||||
@@ -561,13 +569,22 @@ pub(crate) fn in_parent(
|
||||
}
|
||||
// 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 range on this widget's box. A length it pinned is this
|
||||
// widget's length less the part's pixels where the 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), None) => {
|
||||
result.extent[n] = holds.extent[n].through(span.len());
|
||||
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 {
|
||||
Rel::ONE => pinned - Len::from_parts(Rel::ZERO, part_len.px),
|
||||
_ => own.axis(axis).len(),
|
||||
});
|
||||
}
|
||||
// Its box is a part of this widget's frame: a length of the
|
||||
// frame is all that reaches it, so what it holds for is a range
|
||||
// on the frame and none of it on this widget's own box.
|
||||
// Its box is a part of this widget's frame, or a length of it
|
||||
// decided here: a length of the frame is all that reaches it,
|
||||
// so what it holds for is a range on the frame and none of it
|
||||
// on this widget's own box.
|
||||
_ => {
|
||||
result.frame[n] = result.frame[n].and(
|
||||
holds.extent[n]
|
||||
@@ -670,44 +687,34 @@ pub(crate) fn part_of(extent: UiRegion, place: [Place; 2]) -> UiRegion {
|
||||
/// The length a rule gives a child's frame, per axis: a fraction in it is a
|
||||
/// fraction of the frame the child was given, which is the one length the
|
||||
/// rule can mean.
|
||||
pub(crate) fn narrowed_by(declared: [Option<LayoutLen>; 2], frame: UiRegion) -> [Option<Len>; 2] {
|
||||
AXES.map(|axis| {
|
||||
declared[axis as usize]
|
||||
.map(|len| Len::from_parts(len.rel, len.px).within_len(frame.axis(axis).len()))
|
||||
})
|
||||
pub(crate) fn narrowed_by(declared: [Option<LayoutLen>; 2]) -> [Option<Len>; 2] {
|
||||
declared.map(|declared| declared.map(|len| Len::from_parts(len.rel, len.px)))
|
||||
}
|
||||
|
||||
/// The frame a child is asked in and the box its drawing goes in, both in
|
||||
/// the coordinates of the widget asking.
|
||||
/// The frame a child is asked in and the box it is asked in, both in the
|
||||
/// coordinates of the widget asking.
|
||||
///
|
||||
/// `frame` is what the caller said the child's fractions are of, and `part`
|
||||
/// what of the caller's own box the drawing takes. `narrow` is the length a
|
||||
/// declared rule gives the frame, which makes the frame the box the drawing
|
||||
/// goes in -- a rule is what decided where it goes, and there is nothing
|
||||
/// left to place inside it. A caller that narrowed the frame itself said the
|
||||
/// same thing.
|
||||
/// `part` is what of the caller's own box the child is given. `narrow` is a
|
||||
/// length decided for the child's frame -- a rule, a share, a box a sibling
|
||||
/// decided -- which makes the frame the box the child is asked in: that
|
||||
/// length is what decided where it goes, placed in the part by the child's
|
||||
/// alignment. Where nothing narrowed it, the frame is the caller's own and
|
||||
/// the part is the box.
|
||||
///
|
||||
/// The length is the caller's to supply so that a widget asked again gets
|
||||
/// the frame it already has rather than a second resolution of its rule.
|
||||
/// A length rather than a position, so that a child placed again is put back
|
||||
/// in whatever part it is given rather than where it first was.
|
||||
pub(crate) fn frame_and_extent(
|
||||
mut frame: UiRegion,
|
||||
part: UiRegion,
|
||||
narrow: [Option<Len>; 2],
|
||||
align: RegionAlign,
|
||||
) -> (UiRegion, UiRegion) {
|
||||
let mut frame = UiRegion::FULL;
|
||||
let mut extent = part;
|
||||
for (axis, narrow) in AXES.into_iter().zip(narrow) {
|
||||
let span = frame.axis_mut(axis);
|
||||
let narrowed = match narrow {
|
||||
Some(len) => {
|
||||
let slot = part.axis(axis);
|
||||
let start = slot.start + (slot.len() - len).scale(align.axis(axis).rel());
|
||||
*span = UiSpan::new(start, start + len);
|
||||
true
|
||||
}
|
||||
None => *span != UiSpan::FULL,
|
||||
};
|
||||
if narrowed {
|
||||
if let Some(len) = narrow {
|
||||
let slot = part.axis(axis);
|
||||
let start = slot.start + (slot.len() - len).scale(align.axis(axis).rel());
|
||||
*frame.axis_mut(axis) = UiSpan::new(start, start + len);
|
||||
*extent.axis_mut(axis) = UiSpan::FULL;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user