diff --git a/core/src/ui/active.rs b/core/src/ui/active.rs index b5f04a9..9c76e17 100644 --- a/core/src/ui/active.rs +++ b/core/src/ui/active.rs @@ -18,6 +18,9 @@ pub struct ActiveData { /// The original frame in its parent widget's coordinates. Recomposition /// and pixel-length evaluation both follow this chain. pub given_region: UiRegion, + /// The frame it was first asked in, in the same coordinates: the offer's + /// frame, which its parent's placing draw may since have narrowed. + pub offer_region: UiRegion, /// The lengths of the box its parent first asked about it in, as /// lengths of the box the parent was itself offered. Any later box it /// was given was decided knowing its answer, so this is the question diff --git a/core/src/ui/painter.rs b/core/src/ui/painter.rs index c6b06a7..0b7f755 100644 --- a/core/src/ui/painter.rs +++ b/core/src/ui/painter.rs @@ -271,6 +271,14 @@ impl<'a> Painter<'a> { .get(&id.id()) .map_or(given_len, |a| a.offer_len), }; + let offer_region = match first_ask { + true => local, + false => self + .state + .active + .get(&id.id()) + .map_or(local, |a| a.offer_region), + }; let offer_placement = if first_ask { placement } else { @@ -295,6 +303,7 @@ impl<'a> Painter<'a> { region_node, mask: self.mask, given_region: local, + offer_region, offer_len, offer_placement, px, @@ -427,6 +436,7 @@ impl<'a> Painter<'a> { self.offered.push(child.id()); let active = self.state.active.get_mut(&child.id()).unwrap(); active.offer_len = local.size(); + active.offer_region = local; active.offer_placement = placement; } let placement = UiRegion { diff --git a/core/src/ui/render_state.rs b/core/src/ui/render_state.rs index 00359e1..3574bb8 100644 --- a/core/src/ui/render_state.rs +++ b/core/src/ui/render_state.rs @@ -24,6 +24,8 @@ pub(super) struct DrawInfo { pub given_region: UiRegion, /// The original offer's lengths relative to the parent's own offer. pub offer_len: UiVec2, + /// The offer's frame in the parent widget's coordinates. + pub offer_region: UiRegion, pub offer_placement: [Option; 2], /// This ask's box in pixels, and the offer's: one multiply from the /// parent's own, which is where every pixel length in layout comes from. @@ -134,6 +136,7 @@ impl UiRenderState { region_node: false, mask: MaskIdx::NONE, given_region: region, + offer_region: region, offer_len: UiVec2::FULL_SIZE, offer_placement: [None; 2], px, @@ -274,6 +277,7 @@ impl UiRenderState { // same question again from these. active.region = region; active.given_region = info.given_region; + active.offer_region = info.offer_region; active.offer_len = info.offer_len; if info.placement == info.offer_placement && info.px == info.offered_px { active.answer = Some(answer); @@ -478,6 +482,7 @@ impl UiRenderState { region_node: false, mask, given_region: UiRegion::FULL, + offer_region: UiRegion::FULL, offer_len: UiVec2::FULL_SIZE, offer_placement: [None; 2], px, @@ -495,6 +500,7 @@ impl UiRenderState { region, placement, given_region: info.given_region, + offer_region: info.offer_region, offer_len: info.offer_len, offer_placement: info.offer_placement, // Whoever asked writes the answer, if this was the asking. @@ -724,6 +730,7 @@ impl UiRenderState { let active = self.active.get_mut(&id).unwrap(); active.region = region; active.given_region = info.given_region; + active.offer_region = info.offer_region; active.offer_len = info.offer_len; #[cfg(feature = "layout-diagnostics")] { @@ -795,6 +802,7 @@ impl UiRenderState { region_node: active.move_idx != active.parent_move, mask, given_region: child_local, + offer_region: active.offer_region, offer_len: active.offer_len, offer_placement: active.offer_placement, px: child_local.size().to_px(info.px), @@ -934,6 +942,7 @@ impl UiRenderState { region: UiRegion::FULL, placement: UiRegion::FULL, given_region: UiRegion::FULL, + offer_region: UiRegion::FULL, offer_len: UiVec2::FULL_SIZE, offer_placement: [None; 2], answer: None, @@ -1170,16 +1179,6 @@ impl UiRenderState { return true; }; let (given_px, offered_px) = self.asked_px(id); - // Asked again in the box its parent gave it, which is the question - // its parent asked only while that box is as long as the offer. Any - // other box is a different question, so the parent asks it, with the - // mark left on. Lengths and not whole boxes: what a drawing depends - // on is its lengths, so the same lengths elsewhere is one question. - if given_px != offered_px { - self.mark(id, rsc.widgets_mut()); - self.mark(parent, rsc.widgets_mut()); - return false; - } let info = DrawInfo { layer: active.layer, parent: active.parent, @@ -1188,6 +1187,7 @@ impl UiRenderState { region_node: rsc.widgets().is_region_node(id), mask: active.parent_mask, given_region: active.given_region, + offer_region: active.offer_region, offer_len: active.offer_len, offer_placement: active.offer_placement, px: given_px, @@ -1202,14 +1202,33 @@ impl UiRenderState { diag::bump(Counter::LocalRedraws); let old = self.remove(id, false, rsc); - // Refresh the original measurement before restoring the assigned slot. - // Its lengths may differ even though the fraction reference is unchanged. + // Asked again where its parent asked: the offer's frame, composed + // where the given one is, at the offer's lengths and placement. That + // is the question its answer came from, whatever box the parent then + // chose from the answer -- which is often a different frame, since a + // span hands its children its own placement across itself. The + // parent draws in its own frame, or in `FULL` where it is a region + // node. + let parent_frame = match self.active.get(&parent) { + Some(p) if p.move_idx == p.parent_move => p.region, + _ => UiRegion::FULL, + }; + let offer_frame = match info.offer_region == UiRegion::FULL { + true => parent_frame, + false => info.offer_region.within(&parent_frame), + }; let offered = DrawInfo { placement: info.offer_placement, + given_region: info.offer_region, + px: offered_px, ..info }; - let answer = self.draw_inner(id, given, offered, old, false, rsc); - if info.placement != offered.placement { + // Where the given differs from the offer, the first draw is only the + // measurement and the second puts the drawing where the parent did. + let placed_apart = + info.placement != offered.placement || info.px != offered.px || given != offer_frame; + let answer = self.draw_inner(id, offer_frame, offered, old, placed_apart, rsc); + if placed_apart { self.draw_inner(id, given, info, None, false, rsc); } let active = self.active.get_mut(&id).unwrap();