diff --git a/core/src/ui/painter.rs b/core/src/ui/painter.rs index 0859325..86fbf0f 100644 --- a/core/src/ui/painter.rs +++ b/core/src/ui/painter.rs @@ -422,10 +422,19 @@ impl<'a> Painter<'a> { self.extent_own[axis as usize] = holds; } - /// One window axis in pixels. Every length in layout is a length of the - /// window, so this is what one becomes pixels against. - pub fn window_px_len(&self, axis: Axis) -> Px { - self.window.axis(axis) + /// A window length in pixels, which is what every length in layout is + /// measured in. Reading one pins the drawing to this window wherever the + /// length is a fraction of it; one that is only pixels is that many + /// pixels in any window and pins nothing. + pub fn to_px(&mut self, len: Len, axis: Axis) -> Px { + let window = self.window.axis(axis); + if len.rel != Rel::ZERO { + let own = &mut self.frame_own[axis as usize]; + if *own == Holds::ANY { + *own = Holds::at(window); + } + } + len.to_px(window) } /// A validity range already stated about the window. Containers use diff --git a/src/random.rs b/src/random.rs index 870b992..b792266 100644 --- a/src/random.rs +++ b/src/random.rs @@ -122,9 +122,7 @@ impl Widget for Branch { let measured = painter .widget_at(&self.probe, [None; 2], [Place::Within(Part::All), top]) .len(Axis::X); - let px = measured - .apply_leftover() - .to_px(painter.window_px_len(Axis::X)); + let px = painter.to_px(measured.apply_leftover(), Axis::X); let below = Place::Within(Part::From(UiSpan::new(cut, painter.extent_len(Axis::Y)))); let place = [Place::Within(Part::All), below]; diff --git a/src/widget/position/scroll.rs b/src/widget/position/scroll.rs index bca8770..8a29257 100644 --- a/src/widget/position/scroll.rs +++ b/src/widget/position/scroll.rs @@ -16,8 +16,7 @@ impl Widget for Scroll { let answer_len = painter .widget_at(&self.inner, [None; 2], [Place::Fill(Part::All); 2]) .len(self.axis); - let fixed = - Len::from_parts(answer_len.rel, answer_len.px).to_px(painter.window_px_len(self.axis)); + let fixed = painter.to_px(Len::from_parts(answer_len.rel, answer_len.px), self.axis); self.container_len = container_len; self.content_len = fixed.max(container_len); diff --git a/src/widget/position/span.rs b/src/widget/position/span.rs index 5c8bb08..327bf9a 100644 --- a/src/widget/position/span.rs +++ b/src/widget/position/span.rs @@ -68,7 +68,7 @@ impl Widget for Span { // exist at all turns on this. let mut shares = false; if total.leftover > Weight::ZERO { - shares = room.to_px(painter.window_px_len(axis)) > Px::ZERO; + shares = painter.to_px(room, axis) > Px::ZERO; let holds = match shares { true => Holds::from(Px::STEP..=Px::MAX), false => Holds::from(Px::MIN..=Px::ZERO), diff --git a/tests/cases/determinism.rs b/tests/cases/determinism.rs index 4f16d7e..4601f59 100644 --- a/tests/cases/determinism.rs +++ b/tests/cases/determinism.rs @@ -26,9 +26,7 @@ impl Widget for BranchesOnMeasurement { let measured = painter .widget_at(&self.probe, [None; 2], [Place::Within(Part::All), top]) .len(Axis::X); - let px = measured - .apply_leftover() - .to_px(painter.window_px_len(Axis::X)); + let px = painter.to_px(measured.apply_leftover(), Axis::X); let below = Place::Within(Part::From(UiSpan::new(cut, painter.extent_len(Axis::Y)))); let place = [Place::Within(Part::All), below]; diff --git a/tests/cases/unsettled.rs b/tests/cases/unsettled.rs index 4337214..f2e4303 100644 --- a/tests/cases/unsettled.rs +++ b/tests/cases/unsettled.rs @@ -1023,3 +1023,47 @@ fn changing_a_rule_over_two_pads_divides_the_column_again() { assert_same_regions(&warm, &ids, &cold, &cold_ids); } + +/// Six widgets, shrunk from seed 942 at depth 6 (`resize`). A `Branch` asks +/// its probe in the top 40 px of its box and forwards the frame, so the +/// scroll's own box is 40 px tall whatever the window is -- but its content +/// is as tall as the frame, which is the window, and a scroll kept to its +/// end has to be told when that changes. Resolving a length against the +/// window is what reads it, so that is where the dependency is taken. +fn plant_a_window_tall_column_in_a_short_scroll(h: &mut Harness) -> Vec { + let leaf = rect(Color::RED).add(&mut h.rsc); + let column = Span { + children: vec![leaf.add_strong(&mut h.rsc)], + dir: Dir::RIGHT, + gap: Px::ZERO, + } + .height(rel(1.0)) + .add(&mut h.rsc); + let scroll = Scroll::new(column.add_strong(&mut h.rsc), Axis::Y).add(&mut h.rsc); + let wide = rect(Color::BLUE).add(&mut h.rsc); + let narrow = rect(Color::GREEN).add(&mut h.rsc); + let root = Branch { + probe: scroll.add_strong(&mut h.rsc), + wide: wide.add_strong(&mut h.rsc), + narrow: narrow.add_strong(&mut h.rsc), + threshold: 55.0, + } + .add(&mut h.rsc); + h.set_root(root); + vec![leaf.id(), column.id(), scroll.id(), root.id()] +} + +#[test] +fn resizing_under_a_short_scroll_snaps_its_window_tall_content_again() { + let mut warm = Harness::new((1920, 1200)); + let ids = plant_a_window_tall_column_in_a_short_scroll(&mut warm); + warm.frame(); + warm.resize((640, 900)); + warm.frame(); + + let mut cold = Harness::new((640, 900)); + let cold_ids = plant_a_window_tall_column_in_a_short_scroll(&mut cold); + cold.frame(); + + assert_same_regions(&warm, &ids, &cold, &cold_ids); +}