Resolve deferred size comparisons before allocating span slots
This commit is contained in:
1 parent
de1eb7e406
commit
8780b40bb7
23 files changed
+1471
-130
No files matched your search
+161
-12
@@ -2,8 +2,9 @@
|
||||
use crate::layout_diagnostics::{self as diag, Counter};
|
||||
use crate::{
|
||||
Axis, Bounds, Declared, Holds, LayoutHolds, LayoutLen, Len, PlaceDesc, Px, PxVec2, RegionAlign,
|
||||
Rel, RenderedText, RetainedPrimitive, Size, StrongWidget, TextAttrs, TextBuffer, TextureHandle,
|
||||
UiRegion, UiRenderState, UiRsc, UiVec2, Weight, WidgetId, Widgets,
|
||||
Rel, RenderedText, RequestArena, RequestedLen, RetainedPrimitive, Size, SizeRequests,
|
||||
StrongWidget, TextAttrs, TextBuffer, TextureHandle, UiRegion, UiRenderState, UiRsc, UiVec2,
|
||||
Weight, WidgetId, Widgets,
|
||||
render::{
|
||||
GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveInst, PrimitiveKind,
|
||||
TexturePrimitive,
|
||||
@@ -41,6 +42,8 @@ pub struct Painter<'a> {
|
||||
pub(super) children: Vec<WidgetId>,
|
||||
/// The children whose size this widget read while drawing.
|
||||
pub(super) size_deps: Vec<WidgetId>,
|
||||
pub(super) request_deps: Vec<WidgetId>,
|
||||
pub(super) scratch: crate::DrawScratch,
|
||||
/// 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
|
||||
/// reads one, then that one unless it says otherwise, and the rel base or
|
||||
@@ -62,6 +65,106 @@ pub struct Painter<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Painter<'a> {
|
||||
/// Reuses this widget's allocation buffers across draws. Nested painters
|
||||
/// have independent buffers, so discovering a child cannot overwrite them.
|
||||
pub fn with_requests<T>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut Self, &mut Vec<RequestedLen>, &mut Vec<Px>) -> T,
|
||||
) -> T {
|
||||
let mut requests = std::mem::take(&mut self.scratch.requests);
|
||||
let mut lengths = std::mem::take(&mut self.scratch.lengths);
|
||||
requests.clear();
|
||||
lengths.clear();
|
||||
let result = f(self, &mut requests, &mut lengths);
|
||||
self.scratch.requests = requests;
|
||||
self.scratch.lengths = lengths;
|
||||
result
|
||||
}
|
||||
|
||||
/// Discovers a composable request without painting a provisional box.
|
||||
pub fn size_request<W: ?Sized>(
|
||||
&mut self,
|
||||
child: &StrongWidget<W>,
|
||||
axis: Axis,
|
||||
) -> Option<RequestedLen> {
|
||||
self.request_deps.push(child.id());
|
||||
if self.rsc.widgets().size_rules(child.id())[axis].bound() == crate::Bound::ANY
|
||||
&& let Some(len) = self.size_hint(child, axis)
|
||||
{
|
||||
return Some(len.into());
|
||||
}
|
||||
let rel_base = self.rel_base(axis);
|
||||
let mut requests = SizeRequests {
|
||||
arena: &mut self.state.requests,
|
||||
measured: None,
|
||||
widgets: self.rsc.widgets(),
|
||||
dependencies: &mut self.request_deps,
|
||||
rel_base,
|
||||
};
|
||||
let request = requests.widget(child, axis)?;
|
||||
// An intrinsic fixed answer still draws in the offered room and is
|
||||
// moved afterwards. Only a declaration or a share chooses its ask box.
|
||||
(request.has_leftover()
|
||||
|| matches!(
|
||||
self.rsc.widgets().size_rules(child.id())[axis],
|
||||
crate::SizeRule::Request(_)
|
||||
))
|
||||
.then_some(request)
|
||||
}
|
||||
|
||||
/// Completes discovery after a child was measured. Only this call may use
|
||||
/// drawn answers: before the ask they could belong to an obsolete box.
|
||||
pub fn measured_request<W: ?Sized>(
|
||||
&mut self,
|
||||
child: &StrongWidget<W>,
|
||||
axis: Axis,
|
||||
len: LayoutLen,
|
||||
) -> RequestedLen {
|
||||
let rel_base = self.rel_base(axis);
|
||||
let bound = self.rsc.widgets().size_rules(child.id())[axis].bound();
|
||||
let mut requests = SizeRequests {
|
||||
arena: &mut self.state.requests,
|
||||
measured: Some(&self.state.active),
|
||||
widgets: self.rsc.widgets(),
|
||||
dependencies: &mut self.request_deps,
|
||||
rel_base,
|
||||
};
|
||||
match requests.widget(child, axis) {
|
||||
Some(request) if request.linear().is_none() && request.has_leftover() => request,
|
||||
_ if len.leftover > Weight::ZERO => requests.bounded(len.into(), bound),
|
||||
_ => len.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A deferred comparison reads this window when the allocation is solved.
|
||||
pub fn allocate(
|
||||
&mut self,
|
||||
requests: &[RequestedLen],
|
||||
room: Len,
|
||||
axis: Axis,
|
||||
output: &mut Vec<Px>,
|
||||
) {
|
||||
let window = self.window[axis];
|
||||
self.own[axis].window = self.own[axis].window.and(Holds::at(window));
|
||||
output.clear();
|
||||
output.extend(
|
||||
self.state
|
||||
.requests
|
||||
.allocate(requests, room.to_px(window), window),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn minimum_request(&mut self, request: &RequestedLen, axis: Axis) -> Len {
|
||||
match request.linear() {
|
||||
Some(len) => len.without_leftover(),
|
||||
None => {
|
||||
let window = self.window[axis];
|
||||
self.own[axis].window = self.own[axis].window.and(Holds::at(window));
|
||||
Len::from_parts(Rel::ZERO, self.state.requests.minimum(*request, window))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
|
||||
let kind = self.rsc.ui_mut().primitives.kind::<P>();
|
||||
self.write(kind, primitive, region);
|
||||
@@ -195,9 +298,15 @@ impl<'a> Painter<'a> {
|
||||
declared,
|
||||
bounds,
|
||||
holds: ask_holds,
|
||||
} = self
|
||||
.placing()
|
||||
.ask(self.rsc.widgets(), self.window, id.id(), offer);
|
||||
inputs,
|
||||
} = self.placing().ask(
|
||||
self.rsc.widgets(),
|
||||
&mut self.state.requests,
|
||||
self.window,
|
||||
id.id(),
|
||||
offer,
|
||||
);
|
||||
self.own = self.own.and(inputs);
|
||||
let region_node = self.rsc.widgets().is_region_node(id.id());
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
if region_node {
|
||||
@@ -422,9 +531,10 @@ impl<'a> Painter<'a> {
|
||||
/// worth anything, since reading one is also what makes its own size
|
||||
/// depend on it.
|
||||
pub fn has_exact_size(&self, axis: Axis) -> bool {
|
||||
self.rsc.widgets().size_rules(self.id)[axis]
|
||||
.exact()
|
||||
.is_some()
|
||||
matches!(
|
||||
self.rsc.widgets().size_rules(self.id)[axis],
|
||||
crate::SizeRule::Exact(_) | crate::SizeRule::Request(_)
|
||||
)
|
||||
}
|
||||
|
||||
/// This widget's own box in pixels. Reading it makes the drawing one
|
||||
@@ -667,6 +777,9 @@ impl Widgets {
|
||||
/// share included, since a share is a length only to whoever divides one,
|
||||
/// and that is the parent rather than this widget.
|
||||
fn exact_len(&self, id: WidgetId, axis: Axis) -> Option<LayoutLen> {
|
||||
if matches!(self.size_rules(id)[axis], crate::SizeRule::Request(_)) {
|
||||
return None;
|
||||
}
|
||||
self.size_rules(id)[axis].exact().or_else(|| {
|
||||
// A hint still narrows the box where no rule does, which is how a
|
||||
// widget with a natural pixel size -- an image, a gap -- gets that
|
||||
@@ -706,6 +819,9 @@ pub(super) struct Ask {
|
||||
/// drawing it is part of. Kept on the widget asked about rather than on
|
||||
/// the asker because the root has no asker.
|
||||
pub holds: LayoutHolds,
|
||||
/// Inputs read against the parent before declarations choose a new base.
|
||||
/// These belong to the asker; the widget's own holds describe its output box.
|
||||
pub inputs: LayoutHolds,
|
||||
}
|
||||
|
||||
impl Placing {
|
||||
@@ -719,6 +835,7 @@ impl Placing {
|
||||
pub(super) fn ask(
|
||||
&self,
|
||||
widgets: &Widgets,
|
||||
requests: &mut RequestArena,
|
||||
window: PxVec2,
|
||||
id: WidgetId,
|
||||
mut place: PlaceDesc,
|
||||
@@ -726,10 +843,33 @@ impl Placing {
|
||||
let align = widgets.alignment(id);
|
||||
let rules = widgets.size_rules(id);
|
||||
let mut holds = LayoutHolds::ANY;
|
||||
let declared = widgets.declared_lens(id);
|
||||
let mut inputs = LayoutHolds::ANY;
|
||||
let mut declared = widgets.declared_lens(id);
|
||||
let mut bounds = Bounds::ANY;
|
||||
for axis in Axis::BOTH {
|
||||
let base = place.base(axis, self.rel_base);
|
||||
if let crate::SizeRule::Request(request) = &rules[axis] {
|
||||
inputs[axis].rel_base = Some(self.rel_base[axis]);
|
||||
inputs[axis].region_len = Some(self.region[axis].len());
|
||||
inputs[axis].window = Holds::at(window[axis]);
|
||||
let offer = place.of(self.region, align)[axis].len();
|
||||
let len = if place[axis].fit == crate::PlaceFit::Allocated {
|
||||
offer
|
||||
} else {
|
||||
let request = requests.import(request, base);
|
||||
let len = requests
|
||||
.allocate(&[request], offer.to_px(window[axis]), window[axis])
|
||||
.next()
|
||||
.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]));
|
||||
place[axis].rel_base = RelBase::Len(len);
|
||||
declared[axis] = Some(len);
|
||||
holds[axis].rel_base = Some(len);
|
||||
continue;
|
||||
}
|
||||
// A share fills what the pixels and fraction beside it leave of
|
||||
// the box and overflows where they are longer, which is the rule
|
||||
// a span follows with one child. Only the overflow is a box of
|
||||
@@ -748,7 +888,15 @@ impl Placing {
|
||||
// anything. Resolved here because only the ask knows the rel base
|
||||
// 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.
|
||||
bounds[axis] = rules[axis].bound().within_len(base);
|
||||
let bound = rules[axis].bound();
|
||||
if [bound.min, bound.max]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.any(|len| len.rel != Rel::ZERO)
|
||||
{
|
||||
inputs[axis].rel_base = Some(self.rel_base[axis]);
|
||||
}
|
||||
bounds[axis] = bound.within_len(base);
|
||||
}
|
||||
let (rel_base, region) =
|
||||
place.rel_base_and_region(self.region, self.rel_base, declared, align);
|
||||
@@ -759,6 +907,7 @@ impl Placing {
|
||||
declared,
|
||||
bounds,
|
||||
holds,
|
||||
inputs,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -785,7 +934,7 @@ impl Placing {
|
||||
// decided, and a parent that divides one has already given the share
|
||||
// whatever it was owed. Only an offer -- a box with the answer still
|
||||
// to be placed inside it -- is a box a share reads.
|
||||
if place[axis].fills {
|
||||
if place[axis].fit.fills() {
|
||||
return (None, Holds::ANY);
|
||||
}
|
||||
// A share with nothing beside it is the box whatever the box is, so
|
||||
@@ -838,7 +987,7 @@ impl PlaceDesc {
|
||||
let mut placed = region;
|
||||
for axis in Axis::BOTH {
|
||||
let reported = size[axis];
|
||||
if reported.fills(declared[axis], self[axis].fills) {
|
||||
if reported.fills(declared[axis], self[axis].fit.fills()) {
|
||||
continue;
|
||||
}
|
||||
placed[axis] = placed[axis].place(reported.without_leftover(), align[axis]);
|
||||
|
||||
Reference in new issue
Block a user