Say which box a pin reaches the parent through, and derive the offer
The review pass over the two commits before it: - `LayoutHolds`'s comment said a pin does not compose into the parent. It does, where the box it pinned is the parent's own box, which is the one case where the parent's own length is what was pinned. - `DrawInfo::offer` was stored beside the two fields it is computed from. It is a method now, with the open question written where it is asked rather than only in the handoff. - `Painter::own` is `frame_own` beside `extent_own`, since a widget's own box is the extent and the frame is what it is a part of. - One expression for the length a rule gives a frame (`narrowed_by`) and one for what a widget answered (`ActiveData::measured`), each of which had two. - The counter said "the placement it was pinned to" for what is now a length; the deferral in `redraw` named the seeds that made it necessary before the last commit rather than the ones that do now; three comments claimed an inset that does not exist yet. No behaviour change: 108 suite tests, 20 core, the 11 generated cases, the same two shrinker seeds failing at 400/5, and `view` and `tabs` byte-identical to the renders taken before it.
This commit is contained in:
1 parent
0954770ceb
commit
49cec82c1b
6 files changed
+76
-70
No files matched your search
@@ -54,7 +54,7 @@ pub(crate) enum Counter {
|
||||
TextShapes,
|
||||
TextBreaks,
|
||||
GlyphPlacements,
|
||||
OutsidePlacement,
|
||||
OutsidePinnedLen,
|
||||
OutsideFrame,
|
||||
OutsideExtent,
|
||||
}
|
||||
@@ -92,7 +92,7 @@ impl Counter {
|
||||
"text shapes",
|
||||
"text line breaks",
|
||||
"glyph placements",
|
||||
"reuse outside: the placement it was pinned to",
|
||||
"reuse outside: the length it was pinned to",
|
||||
"reuse outside: a frame length",
|
||||
"reuse outside: an extent length",
|
||||
];
|
||||
|
||||
@@ -17,7 +17,7 @@ pub struct ActiveData {
|
||||
pub extent: UiRegion,
|
||||
/// That frame in its parent's frame coordinates, before composition:
|
||||
/// forwarded whole by a transparent container, narrowed by a declared
|
||||
/// length or an inset. Its length is the same on every ask, which is what
|
||||
/// length. Its length is the same on every ask, which is what
|
||||
/// a local redraw relies on to ask its parent's own question again.
|
||||
pub frame: UiRegion,
|
||||
/// What of its parent's extent the drawing was given, and what it was
|
||||
@@ -77,6 +77,14 @@ pub struct ActiveData {
|
||||
}
|
||||
|
||||
impl ActiveData {
|
||||
/// What it answered when its parent measured it, where it has been
|
||||
/// measured at all. Not `size`, which is what its last drawing reported:
|
||||
/// a drawing made in the box that answer chose is answering a different
|
||||
/// question.
|
||||
pub fn measured(&self) -> Option<Size> {
|
||||
self.answer.map(|(size, _)| size)
|
||||
}
|
||||
|
||||
/// Whether what it answered still stands for a frame of these pixel
|
||||
/// lengths. The answer was given in the box its parent first asked
|
||||
/// about, which is what it is checked against -- `holds` on the record
|
||||
|
||||
@@ -9,8 +9,9 @@ const AXES: [Axis; 2] = [Axis::X, Axis::Y];
|
||||
/// The symbolic length is a pin rather than a range: a container places its
|
||||
/// children as lengths of its frame measured from where its own box starts,
|
||||
/// so what it draws turns on that box's length and on nothing about where it
|
||||
/// is. It does not compose into the parent -- a widget pinned this way is
|
||||
/// checked when it is re-placed.
|
||||
/// is. It reaches the parent only where the box it pinned is the parent's
|
||||
/// own; anywhere else the parent chose that length itself, and a widget
|
||||
/// pinned this way is checked when it is re-placed.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct LayoutHolds {
|
||||
pub frame: [Holds; 2],
|
||||
|
||||
+25
-23
@@ -20,7 +20,7 @@ pub struct Painter<'a> {
|
||||
/// What a fraction this widget declares or reports is a fraction of, in
|
||||
/// the coordinates of `move_idx`: forwarded from its parent unchanged
|
||||
/// through a span, a stack or a scroll, and narrowed only by what was
|
||||
/// decided above it -- a declared length, an inset, the root. Its length
|
||||
/// decided above it -- a declared length, or the root. Its length
|
||||
/// is the same on every ask of the widget, which is what keeps a fraction
|
||||
/// under it from being resolved twice.
|
||||
pub(super) frame: UiRegion,
|
||||
@@ -55,8 +55,8 @@ pub struct Painter<'a> {
|
||||
pub(super) size_deps: Vec<WidgetId>,
|
||||
/// What this draw itself read of its frame in pixels, per axis: every
|
||||
/// length until it reads one, then that one, unless it says otherwise.
|
||||
pub(super) own: [Holds; 2],
|
||||
/// The same for its extent.
|
||||
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,
|
||||
@@ -161,8 +161,8 @@ impl<'a> Painter<'a> {
|
||||
/// [`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 -- an inset's margins -- and a
|
||||
/// declared length narrows it here.
|
||||
/// it is for what is decided from above, and a declared length narrows
|
||||
/// it here.
|
||||
///
|
||||
/// `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,
|
||||
@@ -176,14 +176,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 narrow = AXES.map(|axis| {
|
||||
let n = axis as usize;
|
||||
// A rule's fraction is a fraction of the frame the child was
|
||||
// given, which is the one length the rule can mean.
|
||||
declared[n]
|
||||
.map(|len| Len::from_parts(len.rel, len.px).within_len(frame.axis(axis).len()))
|
||||
});
|
||||
let (local, extent) = frame_and_extent(frame, part_of(self.extent, place), narrow, align);
|
||||
let (local, extent) = frame_and_extent(
|
||||
frame,
|
||||
part_of(self.extent, place),
|
||||
narrowed_by(declared, frame),
|
||||
align,
|
||||
);
|
||||
let within = match local == UiRegion::FULL {
|
||||
true => self.frame,
|
||||
false => local.within(&self.frame),
|
||||
@@ -224,10 +222,6 @@ impl<'a> Painter<'a> {
|
||||
part: extent,
|
||||
place,
|
||||
offer_place,
|
||||
// The question its parent measured it by, asked again: the
|
||||
// same widget in the same place, however this draw came
|
||||
// about.
|
||||
offer: place == offer_place,
|
||||
px,
|
||||
},
|
||||
None,
|
||||
@@ -429,7 +423,7 @@ impl<'a> Painter<'a> {
|
||||
/// children. Its own box is a part of this one.
|
||||
pub fn frame_px_len(&mut self, axis: Axis) -> Px {
|
||||
let len = self.px.axis(axis);
|
||||
let own = &mut self.own[axis as usize];
|
||||
let own = &mut self.frame_own[axis as usize];
|
||||
if *own == Holds::ANY {
|
||||
*own = Holds::at(len);
|
||||
}
|
||||
@@ -447,7 +441,7 @@ impl<'a> Painter<'a> {
|
||||
self.label(),
|
||||
self.id
|
||||
);
|
||||
self.own[axis as usize] = holds;
|
||||
self.frame_own[axis as usize] = holds;
|
||||
}
|
||||
|
||||
pub fn text_data(&mut self) -> &mut TextData {
|
||||
@@ -663,10 +657,8 @@ pub(crate) fn placed_extent(
|
||||
placed
|
||||
}
|
||||
|
||||
/// The part of a widget's extent a `place` names, in the coordinates its
|
||||
/// extent is in: a span is measured in frame lengths from where the extent
|
||||
/// starts, so nothing under it depends on where that is, and an extent that
|
||||
/// moved re-places every child by re-adding its start.
|
||||
/// The part of a widget's own box a `place` names, in the coordinates that
|
||||
/// box is in.
|
||||
pub(crate) fn part_of(extent: UiRegion, place: [Place; 2]) -> UiRegion {
|
||||
let mut part = extent;
|
||||
for axis in AXES {
|
||||
@@ -675,6 +667,16 @@ pub(crate) fn part_of(extent: UiRegion, place: [Place; 2]) -> UiRegion {
|
||||
part
|
||||
}
|
||||
|
||||
/// 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()))
|
||||
})
|
||||
}
|
||||
|
||||
/// The frame a child is asked in and the box its drawing goes in, both in
|
||||
/// the coordinates of the widget asking.
|
||||
///
|
||||
|
||||
+33
-36
@@ -1,9 +1,9 @@
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
use crate::layout_diagnostics::{self as diag, Counter, ReuseOutcome, TimerKind};
|
||||
use crate::ui::painter::{declared_lens, frame_and_extent, part_of, placed_extent};
|
||||
use crate::ui::painter::{declared_lens, frame_and_extent, narrowed_by, part_of, placed_extent};
|
||||
use crate::{
|
||||
ActiveData, Axis, DrawLayers, Holds, IdLike, LayoutHolds, LayoutLen, Len, MaskIdx, MoveIdx,
|
||||
Moves, Painter, Part, PixelRegion, Place, PxVec2, Size, StrongWidget, UiRegion, UiRsc, Weight,
|
||||
ActiveData, Axis, DrawLayers, Holds, IdLike, LayoutHolds, LayoutLen, MaskIdx, MoveIdx, Moves,
|
||||
Painter, Part, PixelRegion, Place, PxVec2, Size, StrongWidget, UiRegion, UiRsc, Weight,
|
||||
WidgetId, Widgets,
|
||||
util::{HashMap, Vec2},
|
||||
};
|
||||
@@ -34,9 +34,6 @@ pub(super) struct DrawInfo {
|
||||
/// given at the parent's first ask of it. See [`Place`].
|
||||
pub place: [Place; 2],
|
||||
pub offer_place: [Place; 2],
|
||||
/// Whether this ask is the one the widget's answer is kept from: the
|
||||
/// first box its parent asked about, in the parent's own measuring draw.
|
||||
pub offer: bool,
|
||||
/// The frame in pixels: one multiply from the parent's own, which is
|
||||
/// where every pixel length in layout comes from.
|
||||
pub px: PxVec2,
|
||||
@@ -48,6 +45,20 @@ impl DrawInfo {
|
||||
fn fill(&self) -> [bool; 2] {
|
||||
self.place.map(Place::fills)
|
||||
}
|
||||
|
||||
/// Whether this ask is the one the widget's answer is kept from: the
|
||||
/// same widget in the place its parent measured it by, however this draw
|
||||
/// came about.
|
||||
///
|
||||
/// **Open.** A place is a length from where the asking widget's own box
|
||||
/// starts, so two drawings of that widget -- one in the box its parent
|
||||
/// measured it in, one in the box its own answer chose -- ask their
|
||||
/// children in the same places and different boxes, and this cannot tell
|
||||
/// them apart. Shrinker seeds 2 (`repaint`) and 108 (`reorder`) at depth
|
||||
/// 5 are where that shows.
|
||||
fn offer(&self) -> bool {
|
||||
self.place == self.offer_place
|
||||
}
|
||||
}
|
||||
|
||||
/// What a widget's children are placed in: its own box, the coordinates its
|
||||
@@ -153,7 +164,6 @@ impl UiRenderState {
|
||||
part: UiRegion::FULL,
|
||||
place: [Place::Within(Part::All); 2],
|
||||
offer_place: [Place::Within(Part::All); 2],
|
||||
offer: true,
|
||||
px,
|
||||
}
|
||||
}
|
||||
@@ -209,9 +219,7 @@ impl UiRenderState {
|
||||
/// The root's frame: the window, narrowed by the root's own rules. Its
|
||||
/// extent is that frame, since nothing above it chose anything else.
|
||||
fn root_region(id: WidgetId, widgets: &Widgets) -> UiRegion {
|
||||
let declared = declared_lens(widgets, id);
|
||||
let narrow =
|
||||
AXES.map(|axis| declared[axis as usize].map(|len| Len::from_parts(len.rel, len.px)));
|
||||
let narrow = narrowed_by(declared_lens(widgets, id), UiRegion::FULL);
|
||||
frame_and_extent(
|
||||
UiRegion::FULL,
|
||||
UiRegion::FULL,
|
||||
@@ -257,13 +265,13 @@ impl UiRenderState {
|
||||
// axis the parent left open. The frame itself does not change, so
|
||||
// nothing under it resolves a fraction a second time.
|
||||
//
|
||||
// From the answer it gave when its parent first asked, and not from
|
||||
// what a placing evaluation reported: a drawing made in the box that
|
||||
// answer chose is answering a different question, and placing it by
|
||||
// that would move the box out from under itself.
|
||||
let measured = match info.offer {
|
||||
// From the answer it gave when its parent measured it, and not from
|
||||
// what a placing evaluation reported: placing a drawing by what it
|
||||
// said in the box its own answer chose would move the box out from
|
||||
// under it.
|
||||
let measured = match info.offer() {
|
||||
true => answer.0,
|
||||
false => self.active[&id].answer.map_or(answer.0, |(size, _)| size),
|
||||
false => self.active[&id].measured().unwrap_or(answer.0),
|
||||
};
|
||||
let extent = placed_extent(
|
||||
part,
|
||||
@@ -295,7 +303,7 @@ impl UiRenderState {
|
||||
// the same question again from these.
|
||||
active.frame_abs = frame;
|
||||
active.frame = info.frame;
|
||||
if info.offer {
|
||||
if info.offer() {
|
||||
active.answer = Some(answer);
|
||||
active.offer_place = info.offer_place;
|
||||
active.offer_part = part;
|
||||
@@ -361,7 +369,7 @@ impl UiRenderState {
|
||||
// Only evaluation at the original offer establishes the children's
|
||||
// offers. A placing evaluation must not overwrite that question.
|
||||
let px = info.px;
|
||||
let at_offer = info.offer;
|
||||
let at_offer = info.offer();
|
||||
|
||||
let mut painter = Painter {
|
||||
state: self,
|
||||
@@ -380,7 +388,7 @@ impl UiRenderState {
|
||||
offered: Vec::new(),
|
||||
at_offer,
|
||||
size_deps: Vec::new(),
|
||||
own: [Holds::ANY; 2],
|
||||
frame_own: [Holds::ANY; 2],
|
||||
under: LayoutHolds::ANY,
|
||||
extent_own: [Holds::ANY; 2],
|
||||
answer_under: LayoutHolds::ANY,
|
||||
@@ -417,7 +425,7 @@ impl UiRenderState {
|
||||
offered: _,
|
||||
at_offer: _,
|
||||
size_deps,
|
||||
own,
|
||||
frame_own,
|
||||
under,
|
||||
move_idx,
|
||||
layer,
|
||||
@@ -458,7 +466,7 @@ impl UiRenderState {
|
||||
self.moves.remove(idx);
|
||||
}
|
||||
let own_holds = LayoutHolds {
|
||||
frame: own,
|
||||
frame: frame_own,
|
||||
extent: extent_own,
|
||||
extent_len,
|
||||
};
|
||||
@@ -488,7 +496,6 @@ impl UiRenderState {
|
||||
part: UiRegion::FULL,
|
||||
place: [Place::Within(Part::All); 2],
|
||||
offer_place: [Place::Within(Part::All); 2],
|
||||
offer: false,
|
||||
px,
|
||||
},
|
||||
rsc,
|
||||
@@ -659,7 +666,7 @@ impl UiRenderState {
|
||||
for axis in AXES {
|
||||
let n = axis as usize;
|
||||
if holds.extent_len[n].is_some_and(|pinned| pinned != extent.axis(axis).len()) {
|
||||
diag::bump(Counter::OutsidePlacement);
|
||||
diag::bump(Counter::OutsidePinnedLen);
|
||||
}
|
||||
if !holds.frame[n].contains(info.px.axis(axis)) {
|
||||
diag::bump(Counter::OutsideFrame);
|
||||
@@ -721,13 +728,9 @@ impl UiRenderState {
|
||||
fn place_child(&mut self, child: WidgetId, at: &Placing, rsc: &mut dyn UiRsc) {
|
||||
let active = &self.active[&child];
|
||||
let (frame, part) = Self::re_ask(active, at.extent, active.place);
|
||||
// The answer it gave, and not what its last drawing reported: a
|
||||
// drawing made in the box that answer chose is answering a different
|
||||
// question.
|
||||
let answer = active.answer.map_or(active.size, |(size, _)| size);
|
||||
let extent = placed_extent(
|
||||
part,
|
||||
answer,
|
||||
active.measured().unwrap_or(active.size),
|
||||
active.declared,
|
||||
active.place.map(Place::fills),
|
||||
active.own_align,
|
||||
@@ -744,10 +747,6 @@ impl UiRenderState {
|
||||
part,
|
||||
place: active.place,
|
||||
offer_place: active.offer_place,
|
||||
// Putting it back where it was asked about is that ask again, so
|
||||
// what it answers there is the answer -- and putting it anywhere
|
||||
// else is not, however the box was arrived at.
|
||||
offer: active.place == active.offer_place,
|
||||
px: frame.size().to_px(at.px),
|
||||
};
|
||||
self.place(child, extent, info, rsc);
|
||||
@@ -1197,7 +1196,6 @@ impl UiRenderState {
|
||||
part: Self::re_ask(active, parent_extent, active.place).1,
|
||||
place: active.place,
|
||||
offer_place: active.offer_place,
|
||||
offer: false,
|
||||
px,
|
||||
};
|
||||
// The ask that measured it, asked again: the box it was measured in
|
||||
@@ -1207,7 +1205,6 @@ impl UiRenderState {
|
||||
let offered = DrawInfo {
|
||||
place: info.offer_place,
|
||||
part: active.offer_part,
|
||||
offer: true,
|
||||
..info
|
||||
};
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
@@ -1224,8 +1221,8 @@ impl UiRenderState {
|
||||
// the drawing goes in alone. Removing it -- asking the measuring
|
||||
// question here and placing the answer afterwards -- is what the
|
||||
// transparent-frames plan asks for next, and it does not hold yet:
|
||||
// seeds 104 (align) and 210 (reorder) at depth 5 settle differently
|
||||
// warm and cold without it.
|
||||
// seeds 104 (`align`) and 210 (`reorder`) at depth 5 settle
|
||||
// differently warm and cold without it.
|
||||
if info.part.size() != offered.part.size() {
|
||||
self.mark(id, rsc.widgets_mut());
|
||||
self.mark(parent, rsc.widgets_mut());
|
||||
|
||||
Reference in new issue
Block a user