Say what a child gets of a container's box in that box's own lengths
Three things the measurements asked for, all about how much a box that came from an answer costs. **A part in the box's own coordinates.** Saying "less eleven pixels at the end" in frame lengths from the box's start means reading how long the box is, and a container whose box is its own answer then depends on its own answer: `Pad` drew sixty-four times in one resize frame at seed 13, chasing its own width. `Part::Of` says the same thing as a part of the box, which composes without a length -- pixels are pixels wherever the box lands -- and what a child under it holds for maps back through that part onto the container's own box rather than onto the frame. **One axis of the box at a time.** `extent_len` pinned both axes, so a span dividing one of them held for one length of the other as well, and a resize broke every span whose cross-axis answer moved. **No lazy placement.** Leaving a child's answer to be placed at the end of the parent's draw, rather than as the child answers, was meant to save a recomposition. It costs one instead: the drawing is put in the part first and in the answer's box after, and where it does not hold for both that is two drawings rather than one. Seed 1 at depth 8 went from 391 widget draws on a resize to 29 with it gone. The test that pinned three draws for a numeric leaf in a span goes with it. Seed 1 at depth 8, widget draws / distinct widgets / update, against #18's head and against the commit this branch started from: | phase |e44dea3|34cafb6| here | | --- | --- | --- | --- | | cold | 369/261/10.6 | 463/274/13.3 | 516/288/12.0 | | repaint | 1 | 1 | 1 | | many | 157/95/0.33 | 263/108/0.59 | 187/119/0.52 | | size | 16/12/0.018 | 3/3 | 3/3/0.010 | | scroll | 2/0.002 | 1 | 1/0.004 | | resize | 13/13/0.019 | 22/15/0.032 | 24/76/0.090 | Seed 13 at depth 8 is where the protocol still costs: `many` 1091 draws against #18's 524, and `resize` 2215 against a frame #18 does not draw at all. Both are the same shape -- an answer measured in one box and drawn in another -- and the handoff says where that comes from. Checked: fmt, clippy with -D warnings, 108 suite tests, 20 core tests, the 11 generated cases, and the shrinker at 400 trees of depth 5, which fails seeds 2 (repaint) and 108 (reorder).
This commit is contained in:
1 parent
1956be3f3d
commit
0954770ceb
11 files changed
+126
-214
No files matched your search
+27
-23
@@ -1,7 +1,7 @@
|
|||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
use crate::layout_diagnostics::{self as diag, Counter};
|
use crate::layout_diagnostics::{self as diag, Counter};
|
||||||
use crate::{
|
use crate::{
|
||||||
Axis, Holds, LayoutHolds, LayoutLen, Len, Place, Px, PxVec2, RegionAlign, RenderedText,
|
Axis, Holds, LayoutHolds, LayoutLen, Len, Part, Place, Px, PxVec2, RegionAlign, RenderedText,
|
||||||
RetainedPrimitive, Size, StrongWidget, TextAttrs, TextBuffer, TextData, TextureHandle,
|
RetainedPrimitive, Size, StrongWidget, TextAttrs, TextBuffer, TextData, TextureHandle,
|
||||||
UiRegion, UiRenderState, UiRsc, UiSpan, UiVec2, Weight, WidgetId, Widgets,
|
UiRegion, UiRenderState, UiRsc, UiSpan, UiVec2, Weight, WidgetId, Widgets,
|
||||||
render::{
|
render::{
|
||||||
@@ -151,7 +151,7 @@ impl<'a> Painter<'a> {
|
|||||||
/// one child wants, and what every transparent container passes for the
|
/// one child wants, and what every transparent container passes for the
|
||||||
/// frame.
|
/// frame.
|
||||||
pub fn widget<'s, W: ?Sized>(&'s mut self, id: &'s StrongWidget<W>) -> DrawResult<'s, 'a, W> {
|
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(None); 2])
|
self.widget_at(id, UiRegion::FULL, [Place::Within(Part::All); 2])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws a child, saying what its fractions are of and where its drawing
|
/// Draws a child, saying what its fractions are of and where its drawing
|
||||||
@@ -225,12 +225,12 @@ impl<'a> Painter<'a> {
|
|||||||
place,
|
place,
|
||||||
offer_place,
|
offer_place,
|
||||||
// The question its parent measured it by, asked again: the
|
// The question its parent measured it by, asked again: the
|
||||||
// same box of the same widget, however this draw came about.
|
// same widget in the same place, however this draw came
|
||||||
|
// about.
|
||||||
offer: place == offer_place,
|
offer: place == offer_place,
|
||||||
px,
|
px,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
false,
|
|
||||||
self.rsc,
|
self.rsc,
|
||||||
);
|
);
|
||||||
let compose = |holds| in_parent(holds, local, extent, place, declared);
|
let compose = |holds| in_parent(holds, local, extent, place, declared);
|
||||||
@@ -353,13 +353,15 @@ impl<'a> Painter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The symbolic length of this widget's own box, in the lengths of its
|
/// The symbolic length of this widget's own box along one axis, in the
|
||||||
/// frame that it places its children in. Reading it pins the drawing to
|
/// lengths of its frame that it places its children in. Reading it pins
|
||||||
/// that length -- and to nothing about where the box starts, which is
|
/// the drawing to that length -- and to nothing about where the box
|
||||||
/// what lets a container move without being drawn again.
|
/// starts, which is what lets a container move without being drawn
|
||||||
pub fn extent_len(&mut self) -> UiVec2 {
|
/// again. One axis at a time, because a container that divides one axis
|
||||||
let len = self.extent.size();
|
/// holds for any length of the other.
|
||||||
self.extent_len = [Some(len.x), Some(len.y)];
|
pub fn extent_len(&mut self, axis: Axis) -> Len {
|
||||||
|
let len = self.extent.axis(axis).len();
|
||||||
|
self.extent_len[axis as usize] = Some(len);
|
||||||
len
|
len
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,17 +555,22 @@ pub(crate) fn in_parent(
|
|||||||
let n = axis as usize;
|
let n = axis as usize;
|
||||||
let frame_len = frame.axis(axis).len();
|
let frame_len = frame.axis(axis).len();
|
||||||
result.frame[n] = holds.frame[n].through(frame_len);
|
result.frame[n] = holds.frame[n].through(frame_len);
|
||||||
match (place[n].span(), declared[n]) {
|
match (place[n].part(), declared[n]) {
|
||||||
// Its box is this widget's own, so what it holds for is a range
|
// Its box is this widget's own, or a part of it in that box's
|
||||||
// on this widget's own box -- which is what lets that box move
|
// own lengths: so what it holds for is a range on this widget's
|
||||||
// without a redraw. A length it pinned is this widget's length,
|
// own box, which is what lets that box move without a redraw. A
|
||||||
// and pins the same way: where the part is a span instead, that
|
// length it pinned is this widget's length wherever the part is
|
||||||
// span is this widget's own to decide and nothing has to be
|
// the whole of it, and pins the same way.
|
||||||
// said.
|
(Part::All, None) => {
|
||||||
(None, None) => {
|
|
||||||
result.extent[n] = holds.extent[n];
|
result.extent[n] = holds.extent[n];
|
||||||
result.extent_len[n] = holds.extent_len[n];
|
result.extent_len[n] = holds.extent_len[n];
|
||||||
}
|
}
|
||||||
|
// 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.
|
||||||
|
(Part::Of(span), None) => {
|
||||||
|
result.extent[n] = holds.extent[n].through(span.len());
|
||||||
|
}
|
||||||
// Its box is a part of this widget's frame: a length of the
|
// 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
|
// 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.
|
// on the frame and none of it on this widget's own box.
|
||||||
@@ -663,10 +670,7 @@ pub(crate) fn placed_extent(
|
|||||||
pub(crate) fn part_of(extent: UiRegion, place: [Place; 2]) -> UiRegion {
|
pub(crate) fn part_of(extent: UiRegion, place: [Place; 2]) -> UiRegion {
|
||||||
let mut part = extent;
|
let mut part = extent;
|
||||||
for axis in AXES {
|
for axis in AXES {
|
||||||
if let Some(span) = place[axis as usize].span() {
|
*part.axis_mut(axis) = place[axis as usize].part().of(*extent.axis(axis));
|
||||||
let start = extent.axis(axis).start;
|
|
||||||
*part.axis_mut(axis) = UiSpan::new(start + span.start, start + span.end);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
part
|
part
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-12
@@ -1,23 +1,47 @@
|
|||||||
use crate::{PrimitiveHandle, UiRegion, UiSpan};
|
use crate::{PrimitiveHandle, UiRegion, UiSpan};
|
||||||
|
|
||||||
/// Where a child goes along one axis, as a part of this widget's extent.
|
/// What of a widget's own box a child is given, along one axis.
|
||||||
/// Spans are frame lengths from the extent's start, so a span's slot is
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
/// `from..start` and a moved extent re-places every child by re-adding its
|
pub enum Part {
|
||||||
/// start, exactly. `None` is the whole extent.
|
/// The whole of it.
|
||||||
|
All,
|
||||||
|
/// Frame lengths from where the box starts, which is what a container
|
||||||
|
/// dividing room among its children speaks: a child's report is a length
|
||||||
|
/// of the frame, so the cursor that sums those reports is one too. A
|
||||||
|
/// moved box re-places every child by re-adding its start, exactly.
|
||||||
|
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 frame 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),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Part {
|
||||||
|
/// Where it lands in the coordinates `extent` is in.
|
||||||
|
pub(crate) fn of(self, extent: UiSpan) -> UiSpan {
|
||||||
|
match self {
|
||||||
|
Self::All => extent,
|
||||||
|
Self::From(span) => UiSpan::new(extent.start + span.start, extent.start + span.end),
|
||||||
|
Self::Of(span) => span.within(&extent),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Where a child goes along one axis, as a part of this widget's box.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Place {
|
pub enum Place {
|
||||||
/// The child's answer, aligned inside the part by the child's alignment.
|
/// The child's answer, aligned inside the part by the child's alignment.
|
||||||
Within(Option<UiSpan>),
|
Within(Part),
|
||||||
/// Exactly the part; the answer is not placed inside it again.
|
/// Exactly the part; the answer is not placed inside it again.
|
||||||
Fill(Option<UiSpan>),
|
Fill(Part),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Place {
|
impl Place {
|
||||||
/// The part, where the caller named one rather than giving the whole
|
pub(crate) fn part(self) -> Part {
|
||||||
/// extent.
|
|
||||||
pub(crate) fn span(self) -> Option<UiSpan> {
|
|
||||||
match self {
|
match self {
|
||||||
Self::Within(span) | Self::Fill(span) => span,
|
Self::Within(part) | Self::Fill(part) => part,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,8 +52,8 @@ impl Place {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A primitive as it was written: its box in the widget's extent
|
/// A primitive as it was written: its box in the widget's own box's
|
||||||
/// coordinates, which is what a move of that extent re-composes from.
|
/// coordinates, which is what a move of that box re-composes from.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RetainedPrimitive {
|
pub struct RetainedPrimitive {
|
||||||
pub handle: PrimitiveHandle,
|
pub handle: PrimitiveHandle,
|
||||||
|
|||||||
+20
-103
@@ -3,7 +3,7 @@ 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, part_of, placed_extent};
|
||||||
use crate::{
|
use crate::{
|
||||||
ActiveData, Axis, DrawLayers, Holds, IdLike, LayoutHolds, LayoutLen, Len, MaskIdx, MoveIdx,
|
ActiveData, Axis, DrawLayers, Holds, IdLike, LayoutHolds, LayoutLen, Len, MaskIdx, MoveIdx,
|
||||||
Moves, Painter, PixelRegion, Place, PxVec2, Size, StrongWidget, UiRegion, UiRsc, Weight,
|
Moves, Painter, Part, PixelRegion, Place, PxVec2, Size, StrongWidget, UiRegion, UiRsc, Weight,
|
||||||
WidgetId, Widgets,
|
WidgetId, Widgets,
|
||||||
util::{HashMap, Vec2},
|
util::{HashMap, Vec2},
|
||||||
};
|
};
|
||||||
@@ -151,8 +151,8 @@ impl UiRenderState {
|
|||||||
frame: region,
|
frame: region,
|
||||||
frame_abs: region,
|
frame_abs: region,
|
||||||
part: UiRegion::FULL,
|
part: UiRegion::FULL,
|
||||||
place: [Place::Within(None); 2],
|
place: [Place::Within(Part::All); 2],
|
||||||
offer_place: [Place::Within(None); 2],
|
offer_place: [Place::Within(Part::All); 2],
|
||||||
offer: true,
|
offer: true,
|
||||||
px,
|
px,
|
||||||
}
|
}
|
||||||
@@ -202,7 +202,7 @@ impl UiRenderState {
|
|||||||
if let Some(id) = root {
|
if let Some(id) = root {
|
||||||
let region = Self::root_region(id.id(), rsc.widgets());
|
let region = Self::root_region(id.id(), rsc.widgets());
|
||||||
let info = self.root_info(region);
|
let info = self.root_info(region);
|
||||||
self.draw_inner(id.id(), info, None, true, rsc);
|
self.draw_inner(id.id(), info, None, rsc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +226,6 @@ impl UiRenderState {
|
|||||||
id: WidgetId,
|
id: WidgetId,
|
||||||
info: DrawInfo,
|
info: DrawInfo,
|
||||||
mut old: Option<ActiveData>,
|
mut old: Option<ActiveData>,
|
||||||
place_answer: bool,
|
|
||||||
rsc: &mut dyn UiRsc,
|
rsc: &mut dyn UiRsc,
|
||||||
) -> (Size, LayoutHolds, LayoutHolds) {
|
) -> (Size, LayoutHolds, LayoutHolds) {
|
||||||
let (frame, part) = (info.frame_abs, info.part);
|
let (frame, part) = (info.frame_abs, info.part);
|
||||||
@@ -258,31 +257,21 @@ impl UiRenderState {
|
|||||||
// axis the parent left open. The frame itself does not change, so
|
// axis the parent left open. The frame itself does not change, so
|
||||||
// nothing under it resolves a fraction a second time.
|
// nothing under it resolves a fraction a second time.
|
||||||
//
|
//
|
||||||
// Placing the answer waits for the end of the parent's draw, or for
|
// From the answer it gave when its parent first asked, and not from
|
||||||
// the next ask of this child in it: a child measured in the room its
|
// what a placing evaluation reported: a drawing made in the box that
|
||||||
// parent had left over and then given a slot would otherwise be put
|
// answer chose is answering a different question, and placing it by
|
||||||
// in a box neither of them chose, between the two. Whoever asks with
|
// that would move the box out from under itself.
|
||||||
// nobody to finish it -- the root, a local redraw -- places here.
|
|
||||||
let extent = match place_answer {
|
|
||||||
// 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.
|
|
||||||
true => {
|
|
||||||
let measured = match info.offer {
|
let measured = match info.offer {
|
||||||
true => answer.0,
|
true => answer.0,
|
||||||
false => self.active[&id].answer.map_or(answer.0, |(size, _)| size),
|
false => self.active[&id].answer.map_or(answer.0, |(size, _)| size),
|
||||||
};
|
};
|
||||||
placed_extent(
|
let extent = placed_extent(
|
||||||
part,
|
part,
|
||||||
measured,
|
measured,
|
||||||
declared_lens(rsc.widgets(), id),
|
declared_lens(rsc.widgets(), id),
|
||||||
info.fill(),
|
info.fill(),
|
||||||
align,
|
align,
|
||||||
)
|
);
|
||||||
}
|
|
||||||
false => part,
|
|
||||||
};
|
|
||||||
self.place(id, extent, info, rsc);
|
self.place(id, extent, info, rsc);
|
||||||
|
|
||||||
// On axes the parent filled, measurement and drawing share an extent.
|
// On axes the parent filled, measurement and drawing share an extent.
|
||||||
@@ -468,28 +457,13 @@ impl UiRenderState {
|
|||||||
if let Some(idx) = retired_move {
|
if let Some(idx) = retired_move {
|
||||||
self.moves.remove(idx);
|
self.moves.remove(idx);
|
||||||
}
|
}
|
||||||
// The draw is over, so a child whose box was left to its own answer
|
|
||||||
// gets that box now. Nothing it did is undone by this: a child it
|
|
||||||
// asked twice was placed by the second ask, and one that fills what
|
|
||||||
// it was given is already there. What those drawings need is part of
|
|
||||||
// what this one does, so it comes before the contract is settled.
|
|
||||||
let at = Placing {
|
|
||||||
id,
|
|
||||||
extent,
|
|
||||||
local,
|
|
||||||
px,
|
|
||||||
depth: info.depth,
|
|
||||||
move_idx,
|
|
||||||
mask,
|
|
||||||
};
|
|
||||||
let placed = self.place_answers(&at, &children, rsc);
|
|
||||||
let own_holds = LayoutHolds {
|
let own_holds = LayoutHolds {
|
||||||
frame: own,
|
frame: own,
|
||||||
extent: extent_own,
|
extent: extent_own,
|
||||||
extent_len,
|
extent_len,
|
||||||
};
|
};
|
||||||
let answer_holds = own_holds.and(answer_under);
|
let answer_holds = own_holds.and(answer_under);
|
||||||
let holds = answer_holds.and(under).and(placed);
|
let holds = answer_holds.and(under);
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
holds.contains(px, extent),
|
holds.contains(px, extent),
|
||||||
"'{}' ({id:?}) drew in {px:?}, outside the ranges it reported: {holds:?}",
|
"'{}' ({id:?}) drew in {px:?}, outside the ranges it reported: {holds:?}",
|
||||||
@@ -512,8 +486,8 @@ impl UiRenderState {
|
|||||||
frame: UiRegion::FULL,
|
frame: UiRegion::FULL,
|
||||||
frame_abs: UiRegion::FULL,
|
frame_abs: UiRegion::FULL,
|
||||||
part: UiRegion::FULL,
|
part: UiRegion::FULL,
|
||||||
place: [Place::Within(None); 2],
|
place: [Place::Within(Part::All); 2],
|
||||||
offer_place: [Place::Within(None); 2],
|
offer_place: [Place::Within(Part::All); 2],
|
||||||
offer: false,
|
offer: false,
|
||||||
px,
|
px,
|
||||||
},
|
},
|
||||||
@@ -741,61 +715,6 @@ impl UiRenderState {
|
|||||||
Some(answer)
|
Some(answer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Puts each child of a widget that has just drawn where its answer
|
|
||||||
/// says, for the axes the ask left open. Doing it here rather than as
|
|
||||||
/// each child answers is what keeps a child that is asked twice -- a
|
|
||||||
/// span measuring it and then giving it a slot -- to one drawing and one
|
|
||||||
/// move, instead of a move to its answer in between that nobody asked
|
|
||||||
/// for.
|
|
||||||
/// Answers with what those drawings then depend on, as dependencies of
|
|
||||||
/// this widget: a child placed here may have been drawn again in the box
|
|
||||||
/// its answer chose, and what that drawing needs was not known when the
|
|
||||||
/// child was asked.
|
|
||||||
fn place_answers(
|
|
||||||
&mut self,
|
|
||||||
at: &Placing,
|
|
||||||
children: &[WidgetId],
|
|
||||||
rsc: &mut dyn UiRsc,
|
|
||||||
) -> LayoutHolds {
|
|
||||||
let mut under = LayoutHolds::ANY;
|
|
||||||
for &child in children {
|
|
||||||
let Some(active) = self.active.get(&child) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if active.place.iter().all(|place| place.fills()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
self.place_child(child, at, rsc);
|
|
||||||
under = under.and(self.placed_holds(child));
|
|
||||||
}
|
|
||||||
under
|
|
||||||
}
|
|
||||||
|
|
||||||
/// What a child's finished drawing needs, as a range on the frame of the
|
|
||||||
/// widget that drew it.
|
|
||||||
///
|
|
||||||
/// Only the frame: where the box the child ended up in is the answer it
|
|
||||||
/// gave, that box is a length of this frame and maps back through it
|
|
||||||
/// exactly, and where the parent chose the box instead, the ask already
|
|
||||||
/// said what that costs on the parent's own box.
|
|
||||||
fn placed_holds(&self, child: WidgetId) -> LayoutHolds {
|
|
||||||
let active = &self.active[&child];
|
|
||||||
let mut result = LayoutHolds::ANY;
|
|
||||||
for axis in AXES {
|
|
||||||
let n = axis as usize;
|
|
||||||
let frame_len = active.frame.axis(axis).len();
|
|
||||||
result.frame[n] = active.holds.frame[n].through(frame_len);
|
|
||||||
if !active.place[n].fills() {
|
|
||||||
result.frame[n] = result.frame[n].and(
|
|
||||||
active.holds.extent[n]
|
|
||||||
.through(active.extent.axis(axis).len())
|
|
||||||
.through(frame_len),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Places one child of `at.id` in the box that widget's own box gives
|
/// Places one child of `at.id` in the box that widget's own box gives
|
||||||
/// it: its part of the extent, with its answer placed inside that part
|
/// it: its part of the extent, with its answer placed inside that part
|
||||||
/// where the ask left the axis open.
|
/// where the ask left the axis open.
|
||||||
@@ -1025,8 +944,8 @@ impl UiRenderState {
|
|||||||
frame_abs: UiRegion::FULL,
|
frame_abs: UiRegion::FULL,
|
||||||
extent: UiRegion::FULL,
|
extent: UiRegion::FULL,
|
||||||
frame: UiRegion::FULL,
|
frame: UiRegion::FULL,
|
||||||
place: [Place::Within(None); 2],
|
place: [Place::Within(Part::All); 2],
|
||||||
offer_place: [Place::Within(None); 2],
|
offer_place: [Place::Within(Part::All); 2],
|
||||||
offer_part: UiRegion::FULL,
|
offer_part: UiRegion::FULL,
|
||||||
answer: None,
|
answer: None,
|
||||||
size,
|
size,
|
||||||
@@ -1256,7 +1175,7 @@ impl UiRenderState {
|
|||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
diag::bump(Counter::LocalRedraws);
|
diag::bump(Counter::LocalRedraws);
|
||||||
let old = self.remove(id, false, rsc);
|
let old = self.remove(id, false, rsc);
|
||||||
self.draw_inner(id, info, old, true, rsc);
|
self.draw_inner(id, info, old, rsc);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
let px = self.asked_px(id);
|
let px = self.asked_px(id);
|
||||||
@@ -1315,12 +1234,10 @@ impl UiRenderState {
|
|||||||
let old = self.remove(id, false, rsc);
|
let old = self.remove(id, false, rsc);
|
||||||
// The original measurement is refreshed before the assigned slot is
|
// The original measurement is refreshed before the assigned slot is
|
||||||
// restored: its lengths may differ even though the frame is
|
// restored: its lengths may differ even though the frame is
|
||||||
// unchanged. Nobody above is drawing, so this places its own answer,
|
// unchanged.
|
||||||
// at the slot it has.
|
let answer = self.draw_inner(id, offered, old, rsc);
|
||||||
let places = info.place == info.offer_place && info.part == offered.part;
|
if info.place != info.offer_place || info.part != offered.part {
|
||||||
let answer = self.draw_inner(id, offered, old, places, rsc);
|
self.draw_inner(id, info, None, rsc);
|
||||||
if !places {
|
|
||||||
self.draw_inner(id, info, None, true, rsc);
|
|
||||||
}
|
}
|
||||||
let active = self.active.get_mut(&id).unwrap();
|
let active = self.active.get_mut(&id).unwrap();
|
||||||
// A wider contract does not invalidate the guarantee the parent kept.
|
// A wider contract does not invalidate the guarantee the parent kept.
|
||||||
|
|||||||
+4
-5
@@ -117,16 +117,15 @@ pub struct Branch {
|
|||||||
|
|
||||||
impl Widget for Branch {
|
impl Widget for Branch {
|
||||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||||
let len = painter.extent_len();
|
|
||||||
let cut = Len::from_parts(Rel::ZERO, Px::from_int(40));
|
let cut = Len::from_parts(Rel::ZERO, Px::from_int(40));
|
||||||
let top = Place::Within(Some(UiSpan::new(Len::ZERO, cut)));
|
let top = Place::Within(Part::From(UiSpan::new(Len::ZERO, cut)));
|
||||||
let measured = painter
|
let measured = painter
|
||||||
.widget_at(&self.probe, UiRegion::FULL, [Place::Within(None), top])
|
.widget_at(&self.probe, UiRegion::FULL, [Place::Within(Part::All), top])
|
||||||
.len(Axis::X);
|
.len(Axis::X);
|
||||||
let px = measured.apply_leftover().to_px(painter.px_len(Axis::X));
|
let px = measured.apply_leftover().to_px(painter.px_len(Axis::X));
|
||||||
|
|
||||||
let below = Place::Within(Some(UiSpan::new(cut, len.y)));
|
let below = Place::Within(Part::From(UiSpan::new(cut, painter.extent_len(Axis::Y))));
|
||||||
let place = [Place::Within(None), below];
|
let place = [Place::Within(Part::All), below];
|
||||||
match px > Px::from_f32(self.threshold) {
|
match px > Px::from_f32(self.threshold) {
|
||||||
true => painter.widget_at(&self.wide, UiRegion::FULL, place),
|
true => painter.widget_at(&self.wide, UiRegion::FULL, place),
|
||||||
false => painter.widget_at(&self.narrow, UiRegion::FULL, place),
|
false => painter.widget_at(&self.narrow, UiRegion::FULL, place),
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ impl Widget for Offset {
|
|||||||
// The whole of this widget's box, moved: the frame passes through, so
|
// The whole of this widget's box, moved: the frame passes through, so
|
||||||
// what the child declares or reports means the same as it would
|
// what the child declares or reports means the same as it would
|
||||||
// without the offset.
|
// without the offset.
|
||||||
let len = painter.extent_len();
|
let moved = |len: Len, amt: Len| Place::Within(Part::From(UiSpan::new(amt, len + amt)));
|
||||||
let moved = |len: Len, amt: Len| Place::Within(Some(UiSpan::new(amt, len + amt)));
|
let place = [
|
||||||
let place = [moved(len.x, self.amt.x), moved(len.y, self.amt.y)];
|
moved(painter.extent_len(Axis::X), self.amt.x),
|
||||||
|
moved(painter.extent_len(Axis::Y), self.amt.y),
|
||||||
|
];
|
||||||
painter.widget_at(&self.inner, UiRegion::FULL, place).size()
|
painter.widget_at(&self.inner, UiRegion::FULL, place).size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,16 +17,20 @@ impl Widget for Pad {
|
|||||||
// The padding goes around what it pads: the frame passes through, so
|
// The padding goes around what it pads: the frame passes through, so
|
||||||
// the inner's fractions mean what they would without it, and only
|
// the inner's fractions mean what they would without it, and only
|
||||||
// the box it draws in is moved in by the pixels.
|
// the box it draws in is moved in by the pixels.
|
||||||
let len = painter.extent_len();
|
// A part of this widget's own box, in that box's own lengths: the
|
||||||
let inset = |len: Len, lead: Px, trail: Px| {
|
// padding is pixels, which are the same pixels wherever the box
|
||||||
Place::Within(Some(UiSpan::new(
|
// lands, so nothing here reads how long the box is -- and a box
|
||||||
|
// chosen from this widget's own answer therefore does not feed back
|
||||||
|
// into that answer.
|
||||||
|
let inset = |lead: Px, trail: Px| {
|
||||||
|
Place::Within(Part::Of(UiSpan::new(
|
||||||
Len::from_parts(Rel::ZERO, lead),
|
Len::from_parts(Rel::ZERO, lead),
|
||||||
len - Len::from_parts(Rel::ZERO, trail),
|
Len::from_parts(Rel::ONE, -trail),
|
||||||
)))
|
)))
|
||||||
};
|
};
|
||||||
let place = [
|
let place = [
|
||||||
inset(len.x, self.padding.left, self.padding.right),
|
inset(self.padding.left, self.padding.right),
|
||||||
inset(len.y, self.padding.top, self.padding.bottom),
|
inset(self.padding.top, self.padding.bottom),
|
||||||
];
|
];
|
||||||
let inner = painter.widget_at(&self.inner, UiRegion::FULL, place).size();
|
let inner = painter.widget_at(&self.inner, UiRegion::FULL, place).size();
|
||||||
Size {
|
Size {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ impl Widget for Scroll {
|
|||||||
// Measured in the whole viewport, then drawn at the scrolled offset.
|
// Measured in the whole viewport, then drawn at the scrolled offset.
|
||||||
let whole = UiRegion::FULL;
|
let whole = UiRegion::FULL;
|
||||||
let answer_len = painter
|
let answer_len = painter
|
||||||
.widget_at(&self.inner, whole, [Place::Fill(None); 2])
|
.widget_at(&self.inner, whole, [Place::Fill(Part::All); 2])
|
||||||
.len(self.axis);
|
.len(self.axis);
|
||||||
let content = answer_len.apply_leftover();
|
let content = answer_len.apply_leftover();
|
||||||
self.container_len = container_len;
|
self.container_len = container_len;
|
||||||
@@ -65,7 +65,7 @@ impl Widget for Scroll {
|
|||||||
&self.inner,
|
&self.inner,
|
||||||
whole,
|
whole,
|
||||||
self.axis
|
self.axis
|
||||||
.pair(Place::Fill(Some(content)), Place::Fill(None)),
|
.pair(Place::Fill(Part::From(content)), Place::Fill(Part::All)),
|
||||||
);
|
);
|
||||||
// What it occupies is its box, on both axes: it clips its content to
|
// What it occupies is its box, on both axes: it clips its content to
|
||||||
// that box, so it can neither take less of one nor honestly ask for
|
// that box, so it can neither take less of one nor honestly ask for
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ impl Widget for Span {
|
|||||||
// The row: this span's own box, as a length of the frame its children
|
// The row: this span's own box, as a length of the frame its children
|
||||||
// are laid out against. Its start is nothing's business -- a slot is
|
// are laid out against. Its start is nothing's business -- a slot is
|
||||||
// a length from it -- so what this reads is the length alone.
|
// a length from it -- so what this reads is the length alone.
|
||||||
let far = painter.extent_len().axis(axis);
|
let far = painter.extent_len(axis);
|
||||||
let along = |from: Len, to: Len| match self.dir.sign {
|
let along = |from: Len, to: Len| match self.dir.sign {
|
||||||
Sign::Pos => UiSpan::new(from, to),
|
Sign::Pos => UiSpan::new(from, to),
|
||||||
Sign::Neg => UiSpan::new(far - to, far - from),
|
Sign::Neg => UiSpan::new(far - to, far - from),
|
||||||
@@ -21,7 +21,7 @@ impl Widget for Span {
|
|||||||
// Across itself the child sits where its own alignment says, in the
|
// Across itself the child sits where its own alignment says, in the
|
||||||
// whole of the row: a span is what contains its children there, and
|
// whole of the row: a span is what contains its children there, and
|
||||||
// nothing divides that axis.
|
// nothing divides that axis.
|
||||||
let across = Place::Within(None);
|
let across = Place::Within(Part::All);
|
||||||
// A length for every child before their final slots are chosen. The
|
// A length for every child before their final slots are chosen. The
|
||||||
// frame passes through unchanged, so `rel(0.5)` is half the area this
|
// frame passes through unchanged, so `rel(0.5)` is half the area this
|
||||||
// span was given whatever else is in it and wherever this child sits
|
// span was given whatever else is in it and wherever this child sits
|
||||||
@@ -30,7 +30,7 @@ impl Widget for Span {
|
|||||||
let mut cursor = Len::rel_min();
|
let mut cursor = Len::rel_min();
|
||||||
let mut lens = Vec::with_capacity(self.children.len());
|
let mut lens = Vec::with_capacity(self.children.len());
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
let room = Place::Within(Some(along(cursor, far)));
|
let room = Place::Within(Part::From(along(cursor, far)));
|
||||||
let len = painter
|
let len = painter
|
||||||
.widget_at(child, UiRegion::FULL, axis.pair(room, across))
|
.widget_at(child, UiRegion::FULL, axis.pair(room, across))
|
||||||
.len(axis);
|
.len(axis);
|
||||||
@@ -108,7 +108,7 @@ impl Widget for Span {
|
|||||||
// Along the row the span says where the child goes, and that slot
|
// Along the row the span says where the child goes, and that slot
|
||||||
// is the drawing's box outright rather than something to place an
|
// is the drawing's box outright rather than something to place an
|
||||||
// answer inside again.
|
// answer inside again.
|
||||||
let slot = Place::Fill(Some(along(from, start)));
|
let slot = Place::Fill(Part::From(along(from, start)));
|
||||||
let placed = painter.widget_at(child, UiRegion::FULL, axis.pair(slot, across));
|
let placed = painter.widget_at(child, UiRegion::FULL, axis.pair(slot, across));
|
||||||
if shrinks {
|
if shrinks {
|
||||||
let used = placed.len(!axis);
|
let used = placed.len(!axis);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ impl Widget for Stack {
|
|||||||
Some((i, child)) => {
|
Some((i, child)) => {
|
||||||
painter.child_layer_at(i);
|
painter.child_layer_at(i);
|
||||||
painter
|
painter
|
||||||
.widget_at(child, UiRegion::FULL, [Place::Fill(None); 2])
|
.widget_at(child, UiRegion::FULL, [Place::Fill(Part::All); 2])
|
||||||
.size()
|
.size()
|
||||||
}
|
}
|
||||||
None => Size::LEFTOVER,
|
None => Size::LEFTOVER,
|
||||||
|
|||||||
@@ -21,16 +21,15 @@ struct BranchesOnMeasurement {
|
|||||||
|
|
||||||
impl Widget for BranchesOnMeasurement {
|
impl Widget for BranchesOnMeasurement {
|
||||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||||
let len = painter.extent_len();
|
|
||||||
let cut = Len::from_parts(Rel::ZERO, Px::from_int(40));
|
let cut = Len::from_parts(Rel::ZERO, Px::from_int(40));
|
||||||
let top = Place::Within(Some(UiSpan::new(Len::ZERO, cut)));
|
let top = Place::Within(Part::From(UiSpan::new(Len::ZERO, cut)));
|
||||||
let measured = painter
|
let measured = painter
|
||||||
.widget_at(&self.probe, UiRegion::FULL, [Place::Within(None), top])
|
.widget_at(&self.probe, UiRegion::FULL, [Place::Within(Part::All), top])
|
||||||
.len(Axis::X);
|
.len(Axis::X);
|
||||||
let px = measured.apply_leftover().to_px(painter.px_len(Axis::X));
|
let px = measured.apply_leftover().to_px(painter.px_len(Axis::X));
|
||||||
|
|
||||||
let below = Place::Within(Some(UiSpan::new(cut, len.y)));
|
let below = Place::Within(Part::From(UiSpan::new(cut, painter.extent_len(Axis::Y))));
|
||||||
let place = [Place::Within(None), below];
|
let place = [Place::Within(Part::All), below];
|
||||||
match px > Px::from_f32(self.threshold) {
|
match px > Px::from_f32(self.threshold) {
|
||||||
true => painter.widget_at(&self.wide, UiRegion::FULL, place),
|
true => painter.widget_at(&self.wide, UiRegion::FULL, place),
|
||||||
false => painter.widget_at(&self.narrow, UiRegion::FULL, place),
|
false => painter.widget_at(&self.narrow, UiRegion::FULL, place),
|
||||||
|
|||||||
+13
-50
@@ -217,7 +217,7 @@ impl Widget for FromHint {
|
|||||||
painter.widget_at(
|
painter.widget_at(
|
||||||
&self.inner,
|
&self.inner,
|
||||||
UiRegion::FULL,
|
UiRegion::FULL,
|
||||||
[Place::Within(None), Place::Within(Some(top))],
|
[Place::Within(Part::All), Place::Within(Part::From(top))],
|
||||||
);
|
);
|
||||||
Size::LEFTOVER
|
Size::LEFTOVER
|
||||||
}
|
}
|
||||||
@@ -880,8 +880,8 @@ fn resizing_a_fixed_frame_recomposes_its_contents_without_drawing_them() {
|
|||||||
&self.child,
|
&self.child,
|
||||||
UiRegion::FULL,
|
UiRegion::FULL,
|
||||||
[
|
[
|
||||||
Place::Within(Some(self.region.x)),
|
Place::Within(Part::From(self.region.x)),
|
||||||
Place::Within(Some(self.region.y)),
|
Place::Within(Part::From(self.region.y)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
Size::LEFTOVER
|
Size::LEFTOVER
|
||||||
@@ -940,43 +940,6 @@ fn resizing_a_fixed_frame_recomposes_its_contents_without_drawing_them() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_span_does_not_place_its_measurement_before_assigning_the_childs_slot() {
|
|
||||||
struct MeasuredBox(Rc<Cell<usize>>);
|
|
||||||
|
|
||||||
impl Widget for MeasuredBox {
|
|
||||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
|
||||||
self.0.set(self.0.get() + 1);
|
|
||||||
painter.px_size();
|
|
||||||
painter.primitive(RectPrimitive::color(Color::BLUE));
|
|
||||||
Size::from((100, 50))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut h = Harness::new((400, 200));
|
|
||||||
let draws = Rc::new(Cell::new(0));
|
|
||||||
let leaf = MeasuredBox(draws.clone()).add(&mut h.rsc);
|
|
||||||
h.set_root((leaf,).span(Dir::RIGHT).width(rel(1.0)).height(rel(1.0)));
|
|
||||||
|
|
||||||
assert_eq!(draws.get(), 3);
|
|
||||||
assert_corners!(h, leaf, (0, 75), (100, 125));
|
|
||||||
assert_eq!(
|
|
||||||
primitive_bounds(&h, leaf.id()),
|
|
||||||
vec![h.region(&leaf.id()).unwrap()]
|
|
||||||
);
|
|
||||||
h.frame();
|
|
||||||
assert_eq!(draws.get(), 3);
|
|
||||||
|
|
||||||
h.resize((600, 300));
|
|
||||||
h.frame();
|
|
||||||
assert_eq!(draws.get(), 6);
|
|
||||||
assert_corners!(h, leaf, (0, 125), (100, 175));
|
|
||||||
assert_eq!(
|
|
||||||
primitive_bounds(&h, leaf.id()),
|
|
||||||
vec![h.region(&leaf.id()).unwrap()]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn glyph_origins_compose_identically_when_drawn_and_when_retained() {
|
fn glyph_origins_compose_identically_when_drawn_and_when_retained() {
|
||||||
struct Glyphs {
|
struct Glyphs {
|
||||||
@@ -1006,8 +969,8 @@ fn glyph_origins_compose_identically_when_drawn_and_when_retained() {
|
|||||||
&self.child,
|
&self.child,
|
||||||
self.region,
|
self.region,
|
||||||
[
|
[
|
||||||
Place::Fill(Some(self.extent.x)),
|
Place::Fill(Part::From(self.extent.x)),
|
||||||
Place::Fill(Some(self.extent.y)),
|
Place::Fill(Part::From(self.extent.y)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
Size::LEFTOVER
|
Size::LEFTOVER
|
||||||
@@ -1176,8 +1139,8 @@ fn padding_and_stack_boxes_follow_the_extent_without_drawing_again() {
|
|||||||
&self.child,
|
&self.child,
|
||||||
UiRegion::FULL,
|
UiRegion::FULL,
|
||||||
[
|
[
|
||||||
Place::Fill(Some(self.extent.x)),
|
Place::Fill(Part::From(self.extent.x)),
|
||||||
Place::Fill(Some(self.extent.y)),
|
Place::Fill(Part::From(self.extent.y)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
Size::LEFTOVER
|
Size::LEFTOVER
|
||||||
@@ -1264,11 +1227,11 @@ fn moving_an_extent_child_preserves_the_slot_chosen_from_its_measurement() {
|
|||||||
&self.child,
|
&self.child,
|
||||||
UiRegion::FULL,
|
UiRegion::FULL,
|
||||||
[
|
[
|
||||||
Place::Fill(Some(UiSpan::new(
|
Place::Fill(Part::From(UiSpan::new(
|
||||||
Len::px(self.start),
|
Len::px(self.start),
|
||||||
Len::px(self.start + 200.0),
|
Len::px(self.start + 200.0),
|
||||||
))),
|
))),
|
||||||
Place::Fill(Some(UiSpan::FULL)),
|
Place::Fill(Part::From(UiSpan::FULL)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
Size::LEFTOVER
|
Size::LEFTOVER
|
||||||
@@ -1306,8 +1269,8 @@ fn extent_frames_keep_fractional_reports_and_numeric_dependencies_valid() {
|
|||||||
&self.child,
|
&self.child,
|
||||||
UiRegion::FULL,
|
UiRegion::FULL,
|
||||||
[
|
[
|
||||||
Place::Within(Some(self.region.x)),
|
Place::Within(Part::From(self.region.x)),
|
||||||
Place::Within(Some(self.region.y)),
|
Place::Within(Part::From(self.region.y)),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.size()
|
.size()
|
||||||
@@ -1326,8 +1289,8 @@ fn extent_frames_keep_fractional_reports_and_numeric_dependencies_valid() {
|
|||||||
&self.child,
|
&self.child,
|
||||||
UiRegion::FULL,
|
UiRegion::FULL,
|
||||||
[
|
[
|
||||||
Place::Fill(Some(self.extent.x)),
|
Place::Fill(Part::From(self.extent.x)),
|
||||||
Place::Fill(Some(self.extent.y)),
|
Place::Fill(Part::From(self.extent.y)),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.size(),
|
.size(),
|
||||||
|
|||||||
Reference in new issue
Block a user