Hold a bound's length where it is decided, and say each thing once
A quality sweep over the deferred request system, which no earlier round has reviewed. `Bound::outside` said which end a length fell outside and left the caller to look that end up through `Bound::at`, which `expect`s an end the value it is given does not promise: only the pairing of the two calls kept `at(Shorter)` off a bound with no floor. It already had the length in hand, so it returns that, and `Outside` and `at` go with the state that could panic. `measured_request` pinned the rel base for any bound at all, so a measured share under a cap in pixels was invalidated by a change to a base its answer cannot depend on. That question is `Bound::has_fraction` now, which is also the one `Placing::ask` and `SizeRule::has_fraction` were each writing out over a bare array. The rest is one name where there were several spellings: `Span::gaps`, `Padding::along`, `Plan::drop_bounds` behind one `IRIS_UNBOUNDED` in both rigs that had grown their own, and `Stack::size_request` resolving its sizing child the way its draw already does. `Span`'s placement loop asked three times whether the row was allocated, twice to decide one child's length; one match answers all three, so the allocated and plain rules are read side by side. The buffers `draw_at` now reuses for their capacity are empty only because every path to it drains them in `remove`; a `debug_assert` says so, since a drawing over primitives left in one would record them twice. Comments: `with_requests` named discovery as the hazard where it is a child drawn mid-row, `Painter::allocate` documented the window it holds for rather than what it does, `minimum_request` had none, and the note saying a span carries its children's weight whole -- which is still what the unallocated path does, and still the surprising part -- had been replaced by one about the other path.
This commit is contained in:
1 parent
0e838e9dd1
commit
4cb6f6882a
12 files changed
+187
-155
No files matched your search
+13
-14
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Bound, Len, Outside, Px, REL_SHIFT, fixed::div_toward, fixed::narrow};
|
use crate::{Bound, Len, Px, REL_SHIFT, fixed::div_toward, fixed::narrow};
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
/// The lengths of a box, in pixels, that one drawing of a widget holds for:
|
/// The lengths of a box, in pixels, that one drawing of a widget holds for:
|
||||||
@@ -40,40 +40,39 @@ impl Len {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Bound {
|
impl Bound {
|
||||||
/// Which end of this bound `len` falls outside, and the windows that
|
/// The end of this bound `len` falls outside, which is the length it
|
||||||
/// answer holds for. Nothing where it is inside, which is the answer
|
/// gets instead of its own, and the windows that answer holds for.
|
||||||
/// wherever there is no bound at all.
|
/// Nothing where it is inside, which is the answer wherever there is no
|
||||||
|
/// bound at all.
|
||||||
///
|
///
|
||||||
/// `len` and this bound are lengths of the same thing, whichever that
|
/// `len` and this bound are lengths of the same thing, whichever that
|
||||||
/// is: a box in window lengths wants the bound resolved, and a length a
|
/// is: a box in window lengths wants the bound resolved, and a length a
|
||||||
/// widget declares of its rel base wants it as the rule wrote it. Both
|
/// widget declares of its rel base wants it as the rule wrote it. Both
|
||||||
/// comparisons are in pixels, so each is a question about this window,
|
/// comparisons are in pixels, so each is a question about this window,
|
||||||
/// and the box is decided again on the other side of a crossing.
|
/// and the box is decided again on the other side of a crossing.
|
||||||
pub fn outside(&self, len: Len, window: Px) -> (Option<Outside>, Holds) {
|
pub fn outside(&self, len: Len, window: Px) -> (Option<Len>, Holds) {
|
||||||
let mut outside = None;
|
let mut held = None;
|
||||||
let mut holds = Holds::ANY;
|
let mut holds = Holds::ANY;
|
||||||
let mut held = len;
|
|
||||||
if let Some(min) = self.min {
|
if let Some(min) = self.min {
|
||||||
let (shorter, kept) = min.longer_than(held, window);
|
let (shorter, kept) = min.longer_than(len, window);
|
||||||
holds = holds.and(kept);
|
holds = holds.and(kept);
|
||||||
if shorter {
|
if shorter {
|
||||||
outside = Some(Outside::Shorter);
|
held = Some(min);
|
||||||
held = min;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(max) = self.max {
|
if let Some(max) = self.max {
|
||||||
let (longer, kept) = held.longer_than(max, window);
|
let (longer, kept) = held.unwrap_or(len).longer_than(max, window);
|
||||||
holds = holds.and(kept);
|
holds = holds.and(kept);
|
||||||
if longer {
|
if longer {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
outside.is_none(),
|
held.is_none(),
|
||||||
"a floor of {:?} over a cap of {max:?} bounds nothing",
|
"a floor of {:?} over a cap of {max:?} bounds nothing",
|
||||||
self.min,
|
self.min,
|
||||||
);
|
);
|
||||||
outside = Some(Outside::Longer);
|
held = Some(max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(outside, holds)
|
(held, holds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+37
-30
@@ -1,10 +1,10 @@
|
|||||||
#[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, Bounds, Declared, Holds, LayoutHolds, LayoutLen, Len, PlaceDesc, Px, PxVec2, RegionAlign,
|
Axis, Bound, Bounds, Declared, DrawScratch, Holds, LayoutHolds, LayoutLen, Len, PlaceDesc,
|
||||||
Rel, RenderedText, RequestArena, RequestedLen, RetainedPrimitive, Size, SizeRequests,
|
PlaceFit, Px, PxVec2, RegionAlign, Rel, RenderedText, RequestArena, RequestedLen,
|
||||||
StrongWidget, TextAttrs, TextBuffer, TextureHandle, UiRegion, UiRenderState, UiRsc, UiVec2,
|
RetainedPrimitive, Size, SizeRequests, SizeRule, StrongWidget, TextAttrs, TextBuffer,
|
||||||
Weight, WidgetId, Widgets,
|
TextureHandle, UiRegion, UiRenderState, UiRsc, UiVec2, Weight, WidgetId, Widgets,
|
||||||
render::{
|
render::{
|
||||||
GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveInst, PrimitiveKind,
|
GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveInst, PrimitiveKind,
|
||||||
TexturePrimitive,
|
TexturePrimitive,
|
||||||
@@ -43,7 +43,7 @@ pub struct Painter<'a> {
|
|||||||
/// The children whose size this widget read while drawing.
|
/// The children whose size this widget read while drawing.
|
||||||
pub(super) size_deps: Vec<WidgetId>,
|
pub(super) size_deps: Vec<WidgetId>,
|
||||||
pub(super) request_deps: Vec<WidgetId>,
|
pub(super) request_deps: Vec<WidgetId>,
|
||||||
pub(super) scratch: crate::DrawScratch,
|
pub(super) scratch: DrawScratch,
|
||||||
/// What this draw itself reads, as against what its children's drawings
|
/// What this draw itself reads, as against what its children's drawings
|
||||||
/// hold for: every window and every length of its own region until it
|
/// hold for: every window and every length of its own region until it
|
||||||
/// reads one, then that one unless it says otherwise, and the rel base or
|
/// reads one, then that one unless it says otherwise, and the rel base or
|
||||||
@@ -65,8 +65,9 @@ pub struct Painter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Painter<'a> {
|
impl<'a> Painter<'a> {
|
||||||
/// Reuses this widget's allocation buffers across draws. Nested painters
|
/// Reuses this widget's allocation buffers across draws. A child drawn
|
||||||
/// have independent buffers, so discovering a child cannot overwrite them.
|
/// part way through gets a painter of its own, with buffers of its own,
|
||||||
|
/// so nothing it does while this one is mid-row can reach these.
|
||||||
pub fn with_requests<T>(
|
pub fn with_requests<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
f: impl FnOnce(&mut Self, &mut Vec<RequestedLen>, &mut Vec<Px>) -> T,
|
f: impl FnOnce(&mut Self, &mut Vec<RequestedLen>, &mut Vec<Px>) -> T,
|
||||||
@@ -87,7 +88,7 @@ impl<'a> Painter<'a> {
|
|||||||
child: &StrongWidget<W>,
|
child: &StrongWidget<W>,
|
||||||
axis: Axis,
|
axis: Axis,
|
||||||
) -> Option<RequestedLen> {
|
) -> Option<RequestedLen> {
|
||||||
if self.rsc.widgets().size_rules(child.id())[axis].bound() == crate::Bound::ANY
|
if self.rsc.widgets().size_rules(child.id())[axis].bound() == Bound::ANY
|
||||||
&& let Some(len) = self.size_hint(child, axis)
|
&& let Some(len) = self.size_hint(child, axis)
|
||||||
{
|
{
|
||||||
self.request_deps.push(child.id());
|
self.request_deps.push(child.id());
|
||||||
@@ -107,7 +108,7 @@ impl<'a> Painter<'a> {
|
|||||||
request.has_leftover()
|
request.has_leftover()
|
||||||
|| matches!(
|
|| matches!(
|
||||||
self.rsc.widgets().size_rules(child.id())[axis],
|
self.rsc.widgets().size_rules(child.id())[axis],
|
||||||
crate::SizeRule::Request(_)
|
SizeRule::Request(_)
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
if request.is_some() {
|
if request.is_some() {
|
||||||
@@ -144,19 +145,24 @@ impl<'a> Painter<'a> {
|
|||||||
self.rel_base(axis);
|
self.rel_base(axis);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
let request = if len.leftover > Weight::ZERO {
|
let shares = len.leftover > Weight::ZERO;
|
||||||
requests.bounded(len.into(), bound)
|
let request = match shares {
|
||||||
} else {
|
true => requests.bounded(len.into(), bound),
|
||||||
len.into()
|
false => len.into(),
|
||||||
};
|
};
|
||||||
self.request_deps.truncate(start);
|
self.request_deps.truncate(start);
|
||||||
if len.leftover > Weight::ZERO && bound != crate::Bound::ANY {
|
// Only a bound that is a fraction was read against the rel base; one
|
||||||
|
// in pixels binds at the same length under any of them.
|
||||||
|
if shares && bound.has_fraction() {
|
||||||
self.rel_base(axis);
|
self.rel_base(axis);
|
||||||
}
|
}
|
||||||
request
|
request
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A deferred comparison reads this window when the allocation is solved.
|
/// Divides `room` between `requests`, one length per request in
|
||||||
|
/// `output`. A deferred comparison is decided here, against this window:
|
||||||
|
/// which side of a crossing the solution falls is a question in pixels,
|
||||||
|
/// so the drawing holds only for the window that answered it.
|
||||||
pub fn allocate(
|
pub fn allocate(
|
||||||
&mut self,
|
&mut self,
|
||||||
requests: &[RequestedLen],
|
requests: &[RequestedLen],
|
||||||
@@ -174,6 +180,8 @@ impl<'a> Painter<'a> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The least a request can come to, which is what it takes of the row
|
||||||
|
/// before anything is divided. A comparison is read at no share at all.
|
||||||
pub fn minimum_request(&mut self, request: &RequestedLen, axis: Axis) -> Len {
|
pub fn minimum_request(&mut self, request: &RequestedLen, axis: Axis) -> Len {
|
||||||
match request.linear() {
|
match request.linear() {
|
||||||
Some(len) => len.without_leftover(),
|
Some(len) => len.without_leftover(),
|
||||||
@@ -553,7 +561,7 @@ impl<'a> Painter<'a> {
|
|||||||
pub fn has_exact_size(&self, axis: Axis) -> bool {
|
pub fn has_exact_size(&self, axis: Axis) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self.rsc.widgets().size_rules(self.id)[axis],
|
self.rsc.widgets().size_rules(self.id)[axis],
|
||||||
crate::SizeRule::Exact(_) | crate::SizeRule::Request(_)
|
SizeRule::Exact(_) | SizeRule::Request(_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,7 +805,11 @@ impl Widgets {
|
|||||||
/// share included, since a share is a length only to whoever divides one,
|
/// share included, since a share is a length only to whoever divides one,
|
||||||
/// and that is the parent rather than this widget.
|
/// and that is the parent rather than this widget.
|
||||||
fn exact_len(&self, id: WidgetId, axis: Axis) -> Option<LayoutLen> {
|
fn exact_len(&self, id: WidgetId, axis: Axis) -> Option<LayoutLen> {
|
||||||
if matches!(self.size_rules(id)[axis], crate::SizeRule::Request(_)) {
|
// A request is a length the rule gives, and the hint below must not
|
||||||
|
// narrow the box in its place: what the request comes to is not known
|
||||||
|
// until the parent allocates, and it is the parent's answer, not this
|
||||||
|
// widget's.
|
||||||
|
if matches!(self.size_rules(id)[axis], SizeRule::Request(_)) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.size_rules(id)[axis].exact().or_else(|| {
|
self.size_rules(id)[axis].exact().or_else(|| {
|
||||||
@@ -868,23 +880,22 @@ impl Placing {
|
|||||||
let mut bounds = Bounds::ANY;
|
let mut bounds = Bounds::ANY;
|
||||||
for axis in Axis::BOTH {
|
for axis in Axis::BOTH {
|
||||||
let base = place.base(axis, self.rel_base);
|
let base = place.base(axis, self.rel_base);
|
||||||
if let crate::SizeRule::Request(request) = &rules[axis] {
|
if let SizeRule::Request(request) = &rules[axis] {
|
||||||
inputs[axis].rel_base = Some(self.rel_base[axis]);
|
inputs[axis].rel_base = Some(self.rel_base[axis]);
|
||||||
inputs[axis].region_len = Some(self.region[axis].len());
|
inputs[axis].region_len = Some(self.region[axis].len());
|
||||||
inputs[axis].window = Holds::at(window[axis]);
|
inputs[axis].window = Holds::at(window[axis]);
|
||||||
let offer = place.of(self.region, align)[axis].len();
|
let offer = place.of(self.region, align)[axis].len();
|
||||||
let len = if place[axis].fit == crate::PlaceFit::Allocated {
|
let px = if place[axis].fit == PlaceFit::Allocated {
|
||||||
offer
|
offer.to_px(window[axis])
|
||||||
} else {
|
} else {
|
||||||
let request = requests.import(request, base);
|
let request = requests.import(request, base);
|
||||||
let len = requests
|
holds[axis].window = Holds::at(window[axis]);
|
||||||
|
requests
|
||||||
.allocate(&[request], offer.to_px(window[axis]), window[axis])
|
.allocate(&[request], offer.to_px(window[axis]), window[axis])
|
||||||
.next()
|
.next()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
holds[axis].window = Holds::at(window[axis]);
|
|
||||||
Len::from_parts(Rel::ZERO, len)
|
|
||||||
};
|
};
|
||||||
let len = Len::from_parts(Rel::ZERO, len.to_px(window[axis]));
|
let len = Len::from_parts(Rel::ZERO, px);
|
||||||
place[axis].rel_base = RelBase::Len(len);
|
place[axis].rel_base = RelBase::Len(len);
|
||||||
declared[axis] = Some(len);
|
declared[axis] = Some(len);
|
||||||
holds[axis].rel_base = Some(len);
|
holds[axis].rel_base = Some(len);
|
||||||
@@ -909,11 +920,7 @@ impl Placing {
|
|||||||
// a fraction in it is of. `MaxSize` is the box version, and it is
|
// a fraction in it is of. `MaxSize` is the box version, and it is
|
||||||
// a widget because a widget is drawn again when its box changes.
|
// a widget because a widget is drawn again when its box changes.
|
||||||
let bound = rules[axis].bound();
|
let bound = rules[axis].bound();
|
||||||
if [bound.min, bound.max]
|
if bound.has_fraction() {
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.any(|len| len.rel != Rel::ZERO)
|
|
||||||
{
|
|
||||||
inputs[axis].rel_base = Some(self.rel_base[axis]);
|
inputs[axis].rel_base = Some(self.rel_base[axis]);
|
||||||
}
|
}
|
||||||
bounds[axis] = bound.within_len(base);
|
bounds[axis] = bound.within_len(base);
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ pub enum PlaceFit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PlaceFit {
|
impl PlaceFit {
|
||||||
pub fn fills(self) -> bool {
|
pub fn fills(&self) -> bool {
|
||||||
self != Self::Align
|
!matches!(self, Self::Align)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -349,6 +349,14 @@ impl UiRenderState {
|
|||||||
),
|
),
|
||||||
None => Default::default(),
|
None => Default::default(),
|
||||||
};
|
};
|
||||||
|
// Every one of these is a buffer this widget's last draw filled and
|
||||||
|
// `remove` emptied, kept for its capacity alone. A drawing whose
|
||||||
|
// primitives were still in it would record them twice.
|
||||||
|
debug_assert!(
|
||||||
|
textures.is_empty() && primitives.is_empty() && request_deps.is_empty(),
|
||||||
|
"'{}' ({id:?}) was drawn again over what its last draw left",
|
||||||
|
rsc.widgets().label(id)
|
||||||
|
);
|
||||||
let children = std::mem::take(&mut scratch.children);
|
let children = std::mem::take(&mut scratch.children);
|
||||||
let size_deps = std::mem::take(&mut scratch.size_deps);
|
let size_deps = std::mem::take(&mut scratch.size_deps);
|
||||||
let under = std::mem::take(&mut scratch.under);
|
let under = std::mem::take(&mut scratch.under);
|
||||||
@@ -468,11 +476,10 @@ impl UiRenderState {
|
|||||||
if answer.leftover != Weight::ZERO {
|
if answer.leftover != Weight::ZERO {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let (outside, kept) =
|
let (held, kept) = info.bounds[axis].outside(answer.without_leftover(), window[axis]);
|
||||||
info.bounds[axis].outside(answer.without_leftover(), window[axis]);
|
|
||||||
bounded[axis].window = kept;
|
bounded[axis].window = kept;
|
||||||
if let Some(outside) = outside {
|
if let Some(held) = held {
|
||||||
size[axis] = info.bounds[axis].at(outside).into();
|
size[axis] = held.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// A widget that clipped its contents to its box drew nothing outside
|
// A widget that clipped its contents to its box drew nothing outside
|
||||||
@@ -1031,10 +1038,10 @@ impl UiRenderState {
|
|||||||
// something below is about to change it -- which is the whole class
|
// something below is about to change it -- which is the whole class
|
||||||
// of defect where a widget settles inside its parent's draw, clears
|
// of defect where a widget settles inside its parent's draw, clears
|
||||||
// its mark there, and tells nobody its answer moved.
|
// its mark there, and tells nobody its answer moved.
|
||||||
// A mark made while the walk runs queues itself through `mark`.
|
// A mark made while the walk runs queues itself through `mark`. What
|
||||||
// What ends the walk is still the set
|
// ends the walk is the marks being spent rather than the queue being
|
||||||
// being spent, not the queue, so a mark that reached it another way
|
// empty, so a mark that reached the queue twice, or that was settled
|
||||||
// cannot be left for the next frame.
|
// another way, costs a pop and nothing else.
|
||||||
loop {
|
loop {
|
||||||
for &id in rsc.widgets().needs_redraw.iter() {
|
for &id in rsc.widgets().needs_redraw.iter() {
|
||||||
if !self.deferred.contains(&id) {
|
if !self.deferred.contains(&id) {
|
||||||
|
|||||||
+32
-21
@@ -1,6 +1,10 @@
|
|||||||
use crate::{Axis, LayoutLen, Len, Px, StrongWidget, Weight, WidgetId, Widgets};
|
use crate::{
|
||||||
|
ActiveData, Axis, Bound, LayoutLen, Len, Px, Rel, SizeRule, StrongWidget, UiNum, Weight,
|
||||||
|
WidgetId, Widgets, util::HashMap,
|
||||||
|
};
|
||||||
|
use std::{cmp::Ordering, sync::Arc};
|
||||||
|
|
||||||
impl<N: crate::UiNum> From<N> for SizeRequest {
|
impl<N: UiNum> From<N> for SizeRequest {
|
||||||
fn from(value: N) -> Self {
|
fn from(value: N) -> Self {
|
||||||
LayoutLen::px(value).into()
|
LayoutLen::px(value).into()
|
||||||
}
|
}
|
||||||
@@ -25,9 +29,9 @@ impl LayoutLen {
|
|||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum SizeRequest {
|
pub enum SizeRequest {
|
||||||
Linear(LayoutLen),
|
Linear(LayoutLen),
|
||||||
Sum(std::sync::Arc<(Self, Self)>),
|
Sum(Arc<(Self, Self)>),
|
||||||
Min(std::sync::Arc<(Self, Self)>),
|
Min(Arc<(Self, Self)>),
|
||||||
Max(std::sync::Arc<(Self, Self)>),
|
Max(Arc<(Self, Self)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LayoutLen> for SizeRequest {
|
impl From<LayoutLen> for SizeRequest {
|
||||||
@@ -53,7 +57,7 @@ impl SizeRequest {
|
|||||||
if self == other {
|
if self == other {
|
||||||
self
|
self
|
||||||
} else {
|
} else {
|
||||||
Self::Min(std::sync::Arc::new((self, other)))
|
Self::Min(Arc::new((self, other)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +71,7 @@ impl SizeRequest {
|
|||||||
if self == other {
|
if self == other {
|
||||||
self
|
self
|
||||||
} else {
|
} else {
|
||||||
Self::Max(std::sync::Arc::new((self, other)))
|
Self::Max(Arc::new((self, other)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +86,7 @@ impl std::ops::Add for SizeRequest {
|
|||||||
fn add(self, other: Self) -> Self {
|
fn add(self, other: Self) -> Self {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Self::Linear(a), Self::Linear(b)) => Self::Linear(a + b),
|
(Self::Linear(a), Self::Linear(b)) => Self::Linear(a + b),
|
||||||
(a, b) => Self::Sum(std::sync::Arc::new((a, b))),
|
(a, b) => Self::Sum(Arc::new((a, b))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,13 +117,14 @@ impl From<Len> for RequestedLen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl RequestedLen {
|
impl RequestedLen {
|
||||||
pub fn linear(self) -> Option<LayoutLen> {
|
/// The length itself, where no comparison is waiting on an allocation.
|
||||||
|
pub fn linear(&self) -> Option<LayoutLen> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
RequestValue::Linear(len) => Some(len),
|
RequestValue::Linear(len) => Some(len),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn has_leftover(self) -> bool {
|
pub fn has_leftover(&self) -> bool {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
RequestValue::Linear(len) => len.leftover > Weight::ZERO,
|
RequestValue::Linear(len) => len.leftover > Weight::ZERO,
|
||||||
RequestValue::Deferred { leftover, .. } => leftover,
|
RequestValue::Deferred { leftover, .. } => leftover,
|
||||||
@@ -195,7 +200,7 @@ impl RequestArena {
|
|||||||
fn segment(&self, request: RequestedLen, at: Ratio, window: Px) -> Segment {
|
fn segment(&self, request: RequestedLen, at: Ratio, window: Px) -> Segment {
|
||||||
match request.0 {
|
match request.0 {
|
||||||
RequestValue::Linear(len) => {
|
RequestValue::Linear(len) => {
|
||||||
assert!(
|
debug_assert!(
|
||||||
len.leftover >= Weight::ZERO,
|
len.leftover >= Weight::ZERO,
|
||||||
"a leftover weight cannot be negative"
|
"a leftover weight cannot be negative"
|
||||||
);
|
);
|
||||||
@@ -232,8 +237,11 @@ impl RequestArena {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Allocates one scope of nonnegative shares. Floors can overflow; caps
|
/// Divides `room` between requests whose weights are nonnegative, one
|
||||||
/// can leave unused room. Prefix rounding keeps adjacent slot edges equal.
|
/// length per request. A floor can overflow the room and a cap can leave
|
||||||
|
/// part of it unused, so the lengths need not come to `room`. Each edge
|
||||||
|
/// is rounded from the running total rather than from the length before
|
||||||
|
/// it, so two neighbouring slots meet exactly.
|
||||||
pub(crate) fn allocate<'a>(
|
pub(crate) fn allocate<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
requests: &'a [RequestedLen],
|
requests: &'a [RequestedLen],
|
||||||
@@ -265,6 +273,9 @@ impl RequestArena {
|
|||||||
requests.iter().map(move |request| {
|
requests.iter().map(move |request| {
|
||||||
prefix += self.segment(*request, at, window).value(at);
|
prefix += self.segment(*request, at, window).value(at);
|
||||||
let den = i128::from(at.den);
|
let den = i128::from(at.den);
|
||||||
|
// Half away from zero, which is what `Fixed` rounds a division
|
||||||
|
// to: the two decide the same edge, and a change to one of them
|
||||||
|
// is a change to the other.
|
||||||
let edge = prefix.signum() * ((prefix.abs() + den / 2) / den);
|
let edge = prefix.signum() * ((prefix.abs() + den / 2) / den);
|
||||||
let len = Px::from_raw((edge - previous) as i32);
|
let len = Px::from_raw((edge - previous) as i32);
|
||||||
previous = edge;
|
previous = edge;
|
||||||
@@ -289,7 +300,7 @@ impl Ratio {
|
|||||||
const ZERO: Self = Self { num: 0, den: 1 };
|
const ZERO: Self = Self { num: 0, den: 1 };
|
||||||
|
|
||||||
fn new(num: i64, den: i64) -> Self {
|
fn new(num: i64, den: i64) -> Self {
|
||||||
assert_ne!(den, 0);
|
debug_assert_ne!(den, 0, "a ratio of nothing");
|
||||||
if den < 0 {
|
if den < 0 {
|
||||||
Self {
|
Self {
|
||||||
num: -num,
|
num: -num,
|
||||||
@@ -302,14 +313,14 @@ impl Ratio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for Ratio {
|
impl Ord for Ratio {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
(i128::from(self.num) * i128::from(other.den))
|
(i128::from(self.num) * i128::from(other.den))
|
||||||
.cmp(&(i128::from(other.num) * i128::from(self.den)))
|
.cmp(&(i128::from(other.num) * i128::from(self.den)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Ratio {
|
impl PartialOrd for Ratio {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,7 +366,7 @@ fn first(a: Option<Ratio>, b: Option<Ratio>) -> Option<Ratio> {
|
|||||||
/// expressed in window lengths; `rel_base` supplies the base for declarations.
|
/// expressed in window lengths; `rel_base` supplies the base for declarations.
|
||||||
pub struct SizeRequests<'a> {
|
pub struct SizeRequests<'a> {
|
||||||
pub(crate) arena: &'a mut RequestArena,
|
pub(crate) arena: &'a mut RequestArena,
|
||||||
pub(crate) measured: Option<&'a crate::util::HashMap<WidgetId, crate::ActiveData>>,
|
pub(crate) measured: Option<&'a HashMap<WidgetId, ActiveData>>,
|
||||||
pub(crate) widgets: &'a Widgets,
|
pub(crate) widgets: &'a Widgets,
|
||||||
pub(crate) dependencies: &'a mut Vec<WidgetId>,
|
pub(crate) dependencies: &'a mut Vec<WidgetId>,
|
||||||
pub(crate) rel_base: Len,
|
pub(crate) rel_base: Len,
|
||||||
@@ -380,7 +391,7 @@ impl SizeRequests<'_> {
|
|||||||
self.dependencies.push(child.id());
|
self.dependencies.push(child.id());
|
||||||
let rules = self.widgets.size_rules(child.id());
|
let rules = self.widgets.size_rules(child.id());
|
||||||
let rule = &rules[axis];
|
let rule = &rules[axis];
|
||||||
if let crate::SizeRule::Request(request) = rule {
|
if let SizeRule::Request(request) = rule {
|
||||||
return Some(self.arena.import(request, self.rel_base));
|
return Some(self.arena.import(request, self.rel_base));
|
||||||
}
|
}
|
||||||
if let Some(exact) = rule.exact() {
|
if let Some(exact) = rule.exact() {
|
||||||
@@ -396,7 +407,7 @@ impl SizeRequests<'_> {
|
|||||||
Some(self.bounded(request, rule.bound()))
|
Some(self.bounded(request, rule.bound()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn bounded(&mut self, request: RequestedLen, bound: crate::Bound) -> RequestedLen {
|
pub(crate) fn bounded(&mut self, request: RequestedLen, bound: Bound) -> RequestedLen {
|
||||||
let bound = bound.within_len(self.rel_base);
|
let bound = bound.within_len(self.rel_base);
|
||||||
let request = match bound.min {
|
let request = match bound.min {
|
||||||
Some(min) => self.max(request, min.into()),
|
Some(min) => self.max(request, min.into()),
|
||||||
@@ -422,12 +433,12 @@ impl SizeRequests<'_> {
|
|||||||
self.rel_base.px -= padding;
|
self.rel_base.px -= padding;
|
||||||
let request = self.widget(child, axis);
|
let request = self.widget(child, axis);
|
||||||
self.rel_base = base;
|
self.rel_base = base;
|
||||||
request.map(|request| self.sum(request, Len::from_parts(crate::Rel::ZERO, padding).into()))
|
request.map(|request| self.sum(request, Len::from_parts(Rel::ZERO, padding).into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal fractions keep this valid even when padding makes a rel base negative.
|
// Equal fractions keep this valid even when padding makes a rel base negative.
|
||||||
fn independent_order(a: LayoutLen, b: LayoutLen) -> Option<std::cmp::Ordering> {
|
fn independent_order(a: LayoutLen, b: LayoutLen) -> Option<Ordering> {
|
||||||
if a.rel == b.rel && a.leftover == b.leftover {
|
if a.rel == b.rel && a.leftover == b.leftover {
|
||||||
Some(a.px.cmp(&b.px))
|
Some(a.px.cmp(&b.px))
|
||||||
} else if a.px == b.px && a.rel == b.rel {
|
} else if a.px == b.px && a.rel == b.rel {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::util::impl_axis_index;
|
use crate::util::impl_axis_index;
|
||||||
use crate::{Axis, LayoutLen, Len, Rel, SizeRequest};
|
use crate::{Axis, LayoutLen, Len, Rel, SizeRequest};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// What a widget's length on one axis is, as a rule its parent applies where
|
/// What a widget's length on one axis is, as a rule its parent applies where
|
||||||
/// it draws it rather than an answer the widget gives about itself.
|
/// it draws it rather than an answer the widget gives about itself.
|
||||||
@@ -20,7 +21,7 @@ pub enum SizeRule {
|
|||||||
/// This length, whatever the widget reports.
|
/// This length, whatever the widget reports.
|
||||||
Exact(LayoutLen),
|
Exact(LayoutLen),
|
||||||
/// An exact request whose comparisons await the parent's allocation.
|
/// An exact request whose comparisons await the parent's allocation.
|
||||||
Request(std::sync::Arc<SizeRequest>),
|
Request(Arc<SizeRequest>),
|
||||||
/// At least this long, and otherwise whatever the box gives it.
|
/// At least this long, and otherwise whatever the box gives it.
|
||||||
Min(Len),
|
Min(Len),
|
||||||
/// At most this long.
|
/// At most this long.
|
||||||
@@ -53,12 +54,7 @@ impl SizeRule {
|
|||||||
/// Whether what this rule says is a fraction of the rel base, so that
|
/// Whether what this rule says is a fraction of the rel base, so that
|
||||||
/// the same rule against a different one is a different length.
|
/// the same rule against a different one is a different length.
|
||||||
pub fn has_fraction(&self) -> bool {
|
pub fn has_fraction(&self) -> bool {
|
||||||
let bound = self.bound();
|
self.exact().is_some_and(|len| len.rel != Rel::ZERO) || self.bound().has_fraction()
|
||||||
self.exact().is_some_and(|len| len.rel != Rel::ZERO)
|
|
||||||
|| [bound.min, bound.max]
|
|
||||||
.iter()
|
|
||||||
.flatten()
|
|
||||||
.any(|len| len.rel != Rel::ZERO)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This rule with a floor under it, which is the whole of it where there
|
/// This rule with a floor under it, which is the whole of it where there
|
||||||
@@ -115,13 +111,6 @@ pub struct Bound {
|
|||||||
pub max: Option<Len>,
|
pub max: Option<Len>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Which end of a bound a length fell outside.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Outside {
|
|
||||||
Shorter,
|
|
||||||
Longer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Bound {
|
impl Bound {
|
||||||
/// Every length.
|
/// Every length.
|
||||||
pub const ANY: Self = Self {
|
pub const ANY: Self = Self {
|
||||||
@@ -129,14 +118,13 @@ impl Bound {
|
|||||||
max: None,
|
max: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The end [`Outside`] names, which is the length a widget outside it
|
/// Whether either end is a fraction of the rel base, so that the same
|
||||||
/// gets instead of its own.
|
/// bound against a different one binds at a different length.
|
||||||
pub fn at(&self, outside: Outside) -> Len {
|
pub fn has_fraction(&self) -> bool {
|
||||||
let end = match outside {
|
[self.min, self.max]
|
||||||
Outside::Shorter => self.min,
|
.into_iter()
|
||||||
Outside::Longer => self.max,
|
.flatten()
|
||||||
};
|
.any(|len| len.rel != Rel::ZERO)
|
||||||
end.expect("an end nothing is outside of")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This bound as lengths of the window, from lengths of a rel base that
|
/// This bound as lengths of the window, from lengths of a rel base that
|
||||||
@@ -182,7 +170,7 @@ impl From<SizeRequest> for SizeRule {
|
|||||||
fn from(request: SizeRequest) -> Self {
|
fn from(request: SizeRequest) -> Self {
|
||||||
match request {
|
match request {
|
||||||
SizeRequest::Linear(len) => Self::Exact(len),
|
SizeRequest::Linear(len) => Self::Exact(len),
|
||||||
request => Self::Request(std::sync::Arc::new(request)),
|
request => Self::Request(Arc::new(request)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,6 +342,21 @@ impl Plan {
|
|||||||
at(self);
|
at(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Drops every intrinsic bound from this tree, leaving the rest of it
|
||||||
|
/// -- and the generator's draws -- exactly as they were. That isolates
|
||||||
|
/// the ordinary path from the deferred one over the same shapes, which
|
||||||
|
/// is what says whether a difference is the bounds or the trees.
|
||||||
|
pub fn drop_bounds(&mut self) {
|
||||||
|
self.walk_mut(&mut |node| {
|
||||||
|
let Some(rules) = &mut node.size else { return };
|
||||||
|
for axis in Axis::BOTH {
|
||||||
|
if rules[axis].bound() != Bound::ANY {
|
||||||
|
rules[axis] = SizeRule::Free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// The same tree with `edits` applied, by the indices the generator would
|
/// The same tree with `edits` applied, by the indices the generator would
|
||||||
/// have used for them.
|
/// have used for them.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -7,11 +7,7 @@ pub struct Pad {
|
|||||||
|
|
||||||
impl Widget for Pad {
|
impl Widget for Pad {
|
||||||
fn size_request(&self, requests: &mut SizeRequests, axis: Axis) -> Option<RequestedLen> {
|
fn size_request(&self, requests: &mut SizeRequests, axis: Axis) -> Option<RequestedLen> {
|
||||||
let padding = match axis {
|
requests.inset(&self.inner, axis, self.padding.along(axis))
|
||||||
Axis::X => self.padding.left + self.padding.right,
|
|
||||||
Axis::Y => self.padding.top + self.padding.bottom,
|
|
||||||
};
|
|
||||||
requests.inset(&self.inner, axis, padding)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||||
@@ -30,11 +26,11 @@ impl Widget for Pad {
|
|||||||
let inner = painter.widget_at(&self.inner, self.padding.region()).size();
|
let inner = painter.widget_at(&self.inner, self.padding.region()).size();
|
||||||
Size {
|
Size {
|
||||||
x: LayoutLen {
|
x: LayoutLen {
|
||||||
px: inner.x.px + self.padding.left + self.padding.right,
|
px: inner.x.px + self.padding.along(Axis::X),
|
||||||
..inner.x
|
..inner.x
|
||||||
},
|
},
|
||||||
y: LayoutLen {
|
y: LayoutLen {
|
||||||
px: inner.y.px + self.padding.top + self.padding.bottom,
|
px: inner.y.px + self.padding.along(Axis::Y),
|
||||||
..inner.y
|
..inner.y
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -65,6 +61,16 @@ impl Padding {
|
|||||||
bottom: amt,
|
bottom: amt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Both sides of one axis together, which is what this padding takes
|
||||||
|
/// of a length along it.
|
||||||
|
pub fn along(&self, axis: Axis) -> Px {
|
||||||
|
match axis {
|
||||||
|
Axis::X => self.left + self.right,
|
||||||
|
Axis::Y => self.top + self.bottom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `region` less this padding on each side.
|
/// `region` less this padding on each side.
|
||||||
pub fn region_of(&self, mut region: UiRegion) -> UiRegion {
|
pub fn region_of(&self, mut region: UiRegion) -> UiRegion {
|
||||||
region.x.start.px += self.left;
|
region.x.start.px += self.left;
|
||||||
|
|||||||
+37
-25
@@ -14,11 +14,7 @@ impl Widget for Span {
|
|||||||
// cross-axis length then contributes nothing to the drawn answer.
|
// cross-axis length then contributes nothing to the drawn answer.
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut total = RequestedLen::from(Len::from_parts(
|
let mut total = RequestedLen::from(Len::from_parts(Rel::ZERO, self.gaps()));
|
||||||
Rel::ZERO,
|
|
||||||
self.gap
|
|
||||||
.mul_int(self.children.len().saturating_sub(1) as i32),
|
|
||||||
));
|
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
let child = requests.widget(child, axis)?;
|
let child = requests.widget(child, axis)?;
|
||||||
total = requests.sum(total, child);
|
total = requests.sum(total, child);
|
||||||
@@ -45,9 +41,7 @@ impl Span {
|
|||||||
// the length alone.
|
// the length alone.
|
||||||
let row = painter.region_len(axis);
|
let row = painter.region_len(axis);
|
||||||
self.collect(painter, row, lens, true);
|
self.collect(painter, row, lens, true);
|
||||||
let gaps = self
|
let gaps = self.gaps();
|
||||||
.gap
|
|
||||||
.mul_int(self.children.len().saturating_sub(1) as i32);
|
|
||||||
let fixed = lens
|
let fixed = lens
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(Len::from_parts(Rel::ZERO, gaps), |sum, len| {
|
.try_fold(Len::from_parts(Rel::ZERO, gaps), |sum, len| {
|
||||||
@@ -65,8 +59,8 @@ impl Span {
|
|||||||
if nonlinear {
|
if nonlinear {
|
||||||
painter.allocate(lens, row - Len::from_parts(Rel::ZERO, gaps), axis, values);
|
painter.allocate(lens, row - Len::from_parts(Rel::ZERO, gaps), axis, values);
|
||||||
}
|
}
|
||||||
let allocated = nonlinear.then_some(&values);
|
let allocated = nonlinear.then(|| &values[..]);
|
||||||
let total = match &allocated {
|
let total = match allocated {
|
||||||
Some(allocated) => LayoutLen {
|
Some(allocated) => LayoutLen {
|
||||||
px: allocated.iter().fold(gaps, |sum, len| sum + *len),
|
px: allocated.iter().fold(gaps, |sum, len| sum + *len),
|
||||||
..LayoutLen::ZERO
|
..LayoutLen::ZERO
|
||||||
@@ -106,23 +100,27 @@ impl Span {
|
|||||||
true => fixed + room.scale(Rel::ratio(taken, total.leftover)),
|
true => fixed + room.scale(Rel::ratio(taken, total.leftover)),
|
||||||
};
|
};
|
||||||
for (index, (child, request)) in self.children.iter().zip(lens.iter()).enumerate() {
|
for (index, (child, request)) in self.children.iter().zip(lens.iter()).enumerate() {
|
||||||
let len = match &allocated {
|
// An allocated row already has a length for every child; without
|
||||||
Some(allocated) => LayoutLen {
|
// one the request is the length and the room is divided here.
|
||||||
|
// Either way a child asking for nothing but a part of what is
|
||||||
|
// left over, when nothing is, is not drawn at all -- one that
|
||||||
|
// also asked for pixels or a fraction keeps those and overflows.
|
||||||
|
let (len, shares, nothing_left) = match allocated {
|
||||||
|
Some(allocated) => {
|
||||||
|
let len = LayoutLen {
|
||||||
px: allocated[index],
|
px: allocated[index],
|
||||||
..LayoutLen::ZERO
|
..LayoutLen::ZERO
|
||||||
},
|
|
||||||
None => request.linear().unwrap(),
|
|
||||||
};
|
};
|
||||||
let shares = match &allocated {
|
let shares = request.has_leftover();
|
||||||
Some(_) => request.has_leftover(),
|
(len, shares, shares && len.px == Px::ZERO)
|
||||||
None => len.leftover > Weight::ZERO && has_room,
|
}
|
||||||
|
None => {
|
||||||
|
let len = request.linear().unwrap();
|
||||||
|
let shares = len.leftover > Weight::ZERO && has_room;
|
||||||
|
(len, shares, len.is_only_leftover() && !has_room)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// A child asking for nothing but a part of what is left over,
|
if nothing_left {
|
||||||
// when nothing is, is not drawn at all. One that also asked for
|
|
||||||
// pixels or a fraction keeps those and overflows.
|
|
||||||
if (len.is_only_leftover() && !has_room)
|
|
||||||
|| (allocated.is_some() && shares && len.px == Px::ZERO)
|
|
||||||
{
|
|
||||||
painter.undraw(child);
|
painter.undraw(child);
|
||||||
fixed.px += self.gap;
|
fixed.px += self.gap;
|
||||||
continue;
|
continue;
|
||||||
@@ -160,8 +158,15 @@ impl Span {
|
|||||||
fixed.px += self.gap;
|
fixed.px += self.gap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discovery carries nested requests to the allocating ancestor. The
|
// Where nothing was allocated the weight is carried whole rather
|
||||||
// draw still returns an ordinary Size for callers measuring content.
|
// than collapsed to one share, so nesting spans divides the same
|
||||||
|
// space rather than re-dividing a share of it: four `leftover(1)`
|
||||||
|
// children under two spans under one span get a quarter each, which
|
||||||
|
// one share per level does not give. Resolution happens at the
|
||||||
|
// nearest ancestor with a length, and the root always has one --
|
||||||
|
// or, where a comparison deferred the row, at the ancestor whose
|
||||||
|
// allocation discovery carried these requests to, and `total` is
|
||||||
|
// pixels by the time it gets here.
|
||||||
let ortho = match shrinks {
|
let ortho = match shrinks {
|
||||||
true => ortho,
|
true => ortho,
|
||||||
false => LayoutLen::rel(1.0),
|
false => LayoutLen::rel(1.0),
|
||||||
@@ -171,6 +176,13 @@ impl Span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Span {
|
impl Span {
|
||||||
|
/// What the gaps between this span's children take, which is a length of
|
||||||
|
/// the row before anything is divided.
|
||||||
|
fn gaps(&self) -> Px {
|
||||||
|
self.gap
|
||||||
|
.mul_int(self.children.len().saturating_sub(1) as i32)
|
||||||
|
}
|
||||||
|
|
||||||
fn collect(
|
fn collect(
|
||||||
&self,
|
&self,
|
||||||
painter: &mut Painter,
|
painter: &mut Painter,
|
||||||
|
|||||||
@@ -9,12 +9,15 @@ pub struct Stack {
|
|||||||
|
|
||||||
impl Widget for Stack {
|
impl Widget for Stack {
|
||||||
fn size_request(&self, requests: &mut SizeRequests, axis: Axis) -> Option<RequestedLen> {
|
fn size_request(&self, requests: &mut SizeRequests, axis: Axis) -> Option<RequestedLen> {
|
||||||
match self.size {
|
let sizing = match self.size {
|
||||||
StackSize::Default => Some(LayoutLen::LEFTOVER.into()),
|
StackSize::Default => None,
|
||||||
StackSize::Child(i) => match self.children.get(i) {
|
StackSize::Child(i) => self.children.get(i),
|
||||||
|
};
|
||||||
|
// With nothing sizing it a stack is a share of the box it is given,
|
||||||
|
// which is what its draw answers too.
|
||||||
|
match sizing {
|
||||||
Some(child) => requests.widget(child, axis),
|
Some(child) => requests.widget(child, axis),
|
||||||
None => Some(LayoutLen::LEFTOVER.into()),
|
None => Some(LayoutLen::LEFTOVER.into()),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,15 +129,7 @@ fn rig_edits() -> Edits {
|
|||||||
fn fixture(harness: &mut Harness, seed: u64, depth: usize) -> (StrongWidget, Tree) {
|
fn fixture(harness: &mut Harness, seed: u64, depth: usize) -> (StrongWidget, Tree) {
|
||||||
let mut plan = plan(seed, depth, &rig_edits());
|
let mut plan = plan(seed, depth, &rig_edits());
|
||||||
if env("IRIS_UNBOUNDED", 0_u8) != 0 {
|
if env("IRIS_UNBOUNDED", 0_u8) != 0 {
|
||||||
plan.walk_mut(&mut |node| {
|
plan.drop_bounds();
|
||||||
if let Some(rules) = &mut node.size {
|
|
||||||
for axis in Axis::BOTH {
|
|
||||||
if rules[axis].bound() != Bound::ANY {
|
|
||||||
rules[axis] = SizeRule::Free;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
build(&mut harness.rsc, &plan)
|
build(&mut harness.rsc, &plan)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-12
@@ -8,10 +8,10 @@
|
|||||||
//!
|
//!
|
||||||
//! then the same after, and `diff` the two. A line is one widget: the seed,
|
//! then the same after, and `diff` the two. A line is one widget: the seed,
|
||||||
//! its index in creation order, and its box in window pixels, or `-` where
|
//! its index in creation order, and its box in window pixels, or `-` where
|
||||||
//! it is not drawn.
|
//! it is not drawn. `IRIS_UNBOUNDED=1` drops the trees' intrinsic bounds, as
|
||||||
|
//! in the diagnostics rig, which compares the two paths over the same shapes.
|
||||||
|
|
||||||
use iris::harness::Harness;
|
use iris::harness::Harness;
|
||||||
use iris::prelude::{Axis, Bound, SizeRule};
|
|
||||||
use iris::random::{Edits, build, plan};
|
use iris::random::{Edits, build, plan};
|
||||||
|
|
||||||
fn env<T: std::str::FromStr>(name: &str, fallback: T) -> T {
|
fn env<T: std::str::FromStr>(name: &str, fallback: T) -> T {
|
||||||
@@ -30,16 +30,8 @@ fn every_cold_layout_is_printed() {
|
|||||||
for seed in 1..=seeds {
|
for seed in 1..=seeds {
|
||||||
let mut harness = Harness::new((1920.0, 1200.0));
|
let mut harness = Harness::new((1920.0, 1200.0));
|
||||||
let mut plan = plan(seed, depth, &Edits::default());
|
let mut plan = plan(seed, depth, &Edits::default());
|
||||||
if std::env::var_os("IRIS_DUMP_UNBOUNDED").is_some() {
|
if env("IRIS_UNBOUNDED", 0_u8) != 0 {
|
||||||
plan.walk_mut(&mut |node| {
|
plan.drop_bounds();
|
||||||
if let Some(rules) = &mut node.size {
|
|
||||||
for axis in Axis::BOTH {
|
|
||||||
if rules[axis].bound() != Bound::ANY {
|
|
||||||
rules[axis] = SizeRule::Free;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
let (root, tree) = build(&mut harness.rsc, &plan);
|
let (root, tree) = build(&mut harness.rsc, &plan);
|
||||||
harness.state.root = Some(root);
|
harness.state.root = Some(root);
|
||||||
|
|||||||
Reference in new issue
Block a user