diff --git a/Cargo.toml b/Cargo.toml index 8bc1c30..cb28e7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,12 @@ tokio = { workspace = true, features = ["sync", "rt", "rt-multi-thread", "time"] [workspace] members = ["core", "macro", "rig-input"] +# Full debug info was the bulk of what the linker wrote here and almost none of +# what anything read. `dev` keeps line tables and scopes, which is what stepping +# through an example wants; the tests keep the line tables alone, which is what +# a backtrace reads. Measured when the tests became one target: relinking them +# went from 9.8 s to 7.7 s with these, and target/ from 45 GB to 13 GB with the +# two changes together. [profile.dev] debug = 1 diff --git a/core/src/orientation/align.rs b/core/src/orientation/align.rs index 99d952b..436ae5f 100644 --- a/core/src/orientation/align.rs +++ b/core/src/orientation/align.rs @@ -3,7 +3,7 @@ use crate::{Px, Rel}; use super::*; -#[derive(Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct Align { pub x: Option, pub y: Option, @@ -214,3 +214,4 @@ impl RegionAlign { } impl_axis_index!(RegionAlign => AxisAlign); +impl_axis_index!(Align => Option); diff --git a/core/src/orientation/axis.rs b/core/src/orientation/axis.rs index 3cdef73..7b6cfd3 100644 --- a/core/src/orientation/axis.rs +++ b/core/src/orientation/axis.rs @@ -71,50 +71,5 @@ impl Vec2 { } } -pub const trait AxisT { - fn get() -> Axis; -} - -pub struct XAxis; -const impl AxisT for XAxis { - fn get() -> Axis { - Axis::X - } -} - -pub struct YAxis; -const impl AxisT for YAxis { - fn get() -> Axis { - Axis::Y - } -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct BothAxis { - pub x: T, - pub y: T, -} - -impl BothAxis { - pub const fn axis(&mut self) -> &mut T { - match A::get() { - Axis::X => &mut self.x, - Axis::Y => &mut self.y, - } - } - pub fn take_axis(self) -> T { - match A::get() { - Axis::X => self.x, - Axis::Y => self.y, - } - } - pub fn axis_dyn(&mut self, axis: Axis) -> &mut T { - match axis { - Axis::X => &mut self.x, - Axis::Y => &mut self.y, - } - } -} - impl_axis_index!({const SHIFT: u32} FixedVec2 => Fixed); impl_axis_index!(Vec2 => f32); diff --git a/core/src/ui/painter.rs b/core/src/ui/painter.rs index 843d51a..d593d7f 100644 --- a/core/src/ui/painter.rs +++ b/core/src/ui/painter.rs @@ -291,8 +291,8 @@ impl<'a> Painter<'a> { } } - /// What a widget's rules declare its lengths to be, which whoever draws - /// it resolves into its rel base. Reading them depends on nothing -- the box + /// What a rule or a hint declares a widget's lengths to be, which whoever + /// draws it resolves into its rel base. Reading them depends on nothing -- the box /// that comes of them is kept on the child, and `redraw` compares it /// there. fn declared_lens(&self, id: &StrongWidget) -> Declared { @@ -304,14 +304,7 @@ impl<'a> Painter<'a> { /// against this widget's rel base, which is the rel base a child asked with /// nothing narrowed gets. Asking counts as reading its size. pub fn size_hint(&mut self, id: &StrongWidget, axis: Axis) -> Option { - let widgets = self.rsc.widgets(); - // A rule is the answer where there is one: it wins over whatever the - // widget would draw, so it has to win over what the widget says too. - let hint = widgets.size_rules(id.id())[axis].exact().or_else(|| { - widgets - .get_dyn(id.id()) - .and_then(|widget| widget.size_hint(axis)) - }); + let hint = self.rsc.widgets().exact_len(id.id(), axis); let rel_base = self.rel_base[axis]; let resolved = hint.map(|hint| hint.within_len(rel_base)); #[cfg(feature = "layout-diagnostics")] @@ -647,24 +640,27 @@ impl Painter<'_> { } impl Widgets { - /// What a widget's box is where a rule or its own hint says so outright. - pub(super) fn declared_lens(&self, id: WidgetId) -> Declared { - let rules = self.size_rules(id); - let widget = self.get_dyn(id); - Declared::from_axes(|axis| { - rules[axis].declared().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 size rather than the whole offer. That is the - // offer's business rather than a declaration's, and this - // falls away once a widget occupies its reported size inside - // the box it was offered. - widget - .and_then(|widget| widget.size_hint(axis)) - .and_then(|len| len.declared()) - }) + /// What says a widget's length on one axis without drawing it, if anything + /// does. A rule is the answer where there is one: it wins over whatever the + /// widget would draw, so it has to win over what the widget says too -- a + /// 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 { + 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 + // size rather than the whole offer. That is the offer's business + // rather than a declaration's, and this falls away once a widget + // occupies its reported size inside the box it was offered. + self.get_dyn(id)?.size_hint(axis) }) } + + /// What a widget's box is where a rule or its own hint gives one outright, + /// rather than a share for whoever draws it to divide. + pub(super) fn declared_lens(&self, id: WidgetId) -> Declared { + Declared::from_axes(|axis| self.exact_len(id, axis)?.declared()) + } } impl LayoutLen { diff --git a/core/src/util/arena.rs b/core/src/util/arena.rs index 8107c22..986fd38 100644 --- a/core/src/util/arena.rs +++ b/core/src/util/arena.rs @@ -35,7 +35,7 @@ impl Arena { self.data[i] } - pub(crate) fn get_mut(&mut self, id: Id) -> &mut T { + pub fn get_mut(&mut self, id: Id) -> &mut T { &mut self.data[id.idx()] } } diff --git a/core/src/widget/widgets.rs b/core/src/widget/widgets.rs index 3dc044d..3433689 100644 --- a/core/src/widget/widgets.rs +++ b/core/src/widget/widgets.rs @@ -30,6 +30,14 @@ impl Widgets { !self.needs_redraw.is_empty() } + /// Marks this widget for the next frame to draw again, with nothing about + /// it changed. Taking a widget mutably marks it too, which is the ordinary + /// content-change signal; this is for a change the borrow cannot express, + /// and for asking for the same tree over again. + pub fn mark_for_redraw(&mut self, id: impl IdLike) { + self.needs_redraw.insert(id.id()); + } + pub fn get_dyn(&self, id: WidgetId) -> Option<&dyn Widget> { Some(self.vec.get(id)?.widget.as_ref()) } @@ -41,14 +49,14 @@ impl Widgets { /// get_dyn but dynamic borrow checking of widgets /// lets you do recursive (tree) operations, like the painter does - pub(crate) fn get_dyn_dynamic<'a>(&self, id: WidgetId) -> WidgetWrapper<'a> { + pub(crate) fn get_dyn_dynamic<'a>(&self, id: WidgetId) -> DynBorrower<'a, dyn Widget> { // SAFETY: must guarantee no other mutable references to this widget exist // done through the borrow variable let data = unsafe { forget_mut(to_mut(self.vec.get(id).unwrap())) }; if data.borrowed { panic!("tried to mutably borrow the same widget twice"); } - WidgetWrapper::new(data.widget.as_mut(), &mut data.borrowed) + DynBorrower::new(data.widget.as_mut(), &mut data.borrowed) } pub fn get(&self, id: &I) -> Option<&I::Widget> @@ -154,7 +162,7 @@ impl Widgets { self.needs_redraw.insert(id); } - /// Both axes at once, for a caller holding a pair. + /// Both axes at once. pub fn set_size_rules( &mut self, id: impl IdLike, @@ -188,8 +196,6 @@ impl Default for Widgets { } } -pub type WidgetWrapper<'a> = DynBorrower<'a, dyn Widget>; - impl std::ops::Index for Widgets where I::Widget: Sized + Widget, diff --git a/src/default/attr.rs b/src/default/attr.rs index 33a3d8d..c192255 100644 --- a/src/default/attr.rs +++ b/src/default/attr.rs @@ -15,9 +15,10 @@ where let region = ctx.data.render.window_region(&id).unwrap(); let id_pos = region.top_left; let container_pos = ctx.data.render.window_region(&container).unwrap().top_left; - // The pointer arrives from the platform in floats; everything - // it is compared against is on the grid. - let pos = (PxVec2::from_f32(ctx.data.pos) + container_pos - id_pos).to_f32(); + // The two regions are on the grid and the pointer is not, so the + // step between them is taken there and the pointer keeps the + // precision the platform gave it. + let pos = ctx.data.pos + (container_pos - id_pos).to_f32(); let size = region.size().to_f32(); select( rsc, diff --git a/src/random.rs b/src/random.rs index f4e06cf..16af5d7 100644 --- a/src/random.rs +++ b/src/random.rs @@ -8,23 +8,16 @@ use crate::prelude::*; use std::collections::HashMap; -/// The declared lengths of one widget carrying a size rule, by axis. -pub type Lens = [Option; 2]; - -/// Where one widget carrying an alignment sits, by axis. `None` uses the -/// centered default. -pub type Aligns = [Option; 2]; - /// What a test changes between two trees grown from the same seed, so the /// warm one can be mutated and the cold one grown that way to begin with. #[derive(Default)] pub struct Edits { /// Declared sizes, by the order the rules were put on. - pub sizes: HashMap, + pub sizes: HashMap, /// Which children a span has, by the order the spans were made. pub spans: HashMap, /// Alignments, by the order they were put on. - pub aligns: HashMap, + pub aligns: HashMap, /// Which widgets own a movable region, by the order they were offered /// one. Region nodes change what a move writes and how deep a primitive's /// chain is, so a tree that never grows one leaves both untested. @@ -176,9 +169,11 @@ pub struct Plan { /// it one and the offer is taken or declined; a second offer to the same /// widget is dropped, because two rules on one widget would settle in the /// order they were applied rather than in grow order. - pub size: Option, - /// The alignment it carries, under the same one-offer rule. - pub align: Option, + pub size: Option, + /// The alignment it carries, under the same one-offer rule. An axis left + /// out takes the centered default, which is what [`RegionAlign`] reads it + /// as. + pub align: Option, /// Whether it was offered a movable region of its own and what it /// answered. `Some(false)` is an offer declined, which still uses up the /// one offer, where `None` is an offer never made. @@ -651,7 +646,7 @@ impl Sow<'_> { } } - fn align(&mut self) -> Aligns { + fn align(&mut self) -> Align { let axis = |s: &mut Self| match s.rng.below(4) { 0 => None, 1 => Some(AxisAlign::NEG), @@ -661,15 +656,21 @@ impl Sow<'_> { let (x, y) = (axis(self), axis(self)); // Aligning on neither axis leaves the branch unexercised. match x.is_none() && y.is_none() { - true => [Some(AxisAlign::CENTER), y], - false => [x, y], + true => Align { + x: Some(AxisAlign::CENTER), + y, + }, + false => Align { x, y }, } } /// A declared size over half the tree, kept where a test can change it. fn sized(&mut self, inner: &mut Plan) { let take = self.rng.chance(); - let lens = [self.len(), self.len()]; + let lens = SizeRules { + x: self.len().into(), + y: self.len().into(), + }; if !take || inner.size.is_some() { return; } @@ -807,16 +808,14 @@ impl Build<'_, Rsc> { let built = self.kind(&plan.kind); let id = built.id(); if let Some(lens) = plan.size { - self.rsc - .ui_mut() - .widgets - .set_size_rules(id, lens[0], lens[1]); + self.rsc.ui_mut().widgets.set_size_rules(id, lens.x, lens.y); self.tree.sized.push(id); } if let Some(align) = plan.align { + let resolved = RegionAlign::from(align); let widgets = &mut self.rsc.ui_mut().widgets; - for (axis, align) in [Axis::X, Axis::Y].into_iter().zip(align) { - widgets.set_alignment(id, axis, align.unwrap_or_default()); + for axis in Axis::BOTH { + widgets.set_alignment(id, axis, resolved[axis]); } self.tree.aligned.push(id); } diff --git a/src/widget/trait_fns.rs b/src/widget/trait_fns.rs index bf7efad..620b78e 100644 --- a/src/widget/trait_fns.rs +++ b/src/widget/trait_fns.rs @@ -19,8 +19,8 @@ widget_trait! { move |state| { let id = self.add(state); let widgets = &mut state.ui_mut().widgets; - for (axis, align) in [(Axis::X, align.x), (Axis::Y, align.y)] { - if let Some(align) = align { + for axis in Axis::BOTH { + if let Some(align) = align[axis] { widgets.set_alignment(id, axis, align); } } diff --git a/src/widget/wrapper.rs b/src/widget/wrapper.rs index 9ed3cfa..de39302 100644 --- a/src/widget/wrapper.rs +++ b/src/widget/wrapper.rs @@ -9,6 +9,7 @@ use std::marker::Unsize; /// /// Its child is optional so it can also be the swappable slot a tab bar /// needs, which is what it was written for. +#[derive(Default)] pub struct Wrapper { pub inner: Option, } @@ -26,11 +27,7 @@ impl Wrapper { pub fn new() -> Self { Self::default() } - pub fn empty() -> Self { - Self { - inner: Default::default(), - } - } + pub fn set>(&mut self, to: StrongWidget) { self.inner = Some(to) } @@ -42,9 +39,3 @@ impl Wrapper { self.inner.replace(to) } } - -impl Default for Wrapper { - fn default() -> Self { - Self::empty() - } -} diff --git a/tests/cases/determinism.rs b/tests/cases/determinism.rs index 8a5bfe1..126eb58 100644 --- a/tests/cases/determinism.rs +++ b/tests/cases/determinism.rs @@ -69,8 +69,8 @@ fn a_branch_taken_on_a_measurement_holds_across_repaints() { assert_ne!(first, (false, false), "threshold {threshold}: neither drew"); for frame in 0..4 { - h.rsc.widgets_mut().get_dyn_mut(wide); - h.rsc.widgets_mut().get_dyn_mut(narrow); + h.rsc.widgets_mut().mark_for_redraw(wide); + h.rsc.widgets_mut().mark_for_redraw(narrow); h.frame(); assert_eq!( taken(&h, wide, narrow), @@ -88,7 +88,7 @@ fn a_branch_taken_on_a_measurement_is_the_one_a_cold_start_takes() { let (wide, narrow) = plant(&mut warm, threshold); warm.resize((640, 480)); warm.frame(); - warm.rsc.widgets_mut().get_dyn_mut(wide); + warm.rsc.widgets_mut().mark_for_redraw(wide); warm.frame(); let mut cold = Harness::new((640, 480)); diff --git a/tests/cases/idempotence.rs b/tests/cases/idempotence.rs index 2f8423f..52b16e7 100644 --- a/tests/cases/idempotence.rs +++ b/tests/cases/idempotence.rs @@ -18,7 +18,7 @@ fn a_wrapping_text_in_a_span_settles_on_one_width() { let r = h.region(&t.id()).unwrap(); widths.push(r.bot_right.x - r.top_left.x); // Redrawing it changes nothing about the state, so nothing may move. - h.rsc.widgets_mut().get_dyn_mut(t.id()); + h.rsc.widgets_mut().mark_for_redraw(t.id()); h.frame(); } println!("widths over six frames: {widths:?}"); diff --git a/tests/cases/layout.rs b/tests/cases/layout.rs index 2435775..e6ac7c4 100644 --- a/tests/cases/layout.rs +++ b/tests/cases/layout.rs @@ -1,5 +1,7 @@ //! Where a frame puts things, with no window to put them in. +use std::{cell::Cell, rc::Rc}; + use iris::harness::{Harness, assert_corners}; use iris::prelude::*; @@ -219,6 +221,45 @@ fn an_empty_widget_takes_a_share_of_a_span() { assert_corners!(h, right, (300, 0), (400, 200)); } +/// A widget with a natural pixel size, like an image, which records the box +/// it was asked in so a test can see which length decided it. +struct NaturalSize { + len: f32, + asked: Rc>, +} + +impl Widget for NaturalSize { + fn draw(&mut self, painter: &mut Painter) -> Size { + self.asked.set(painter.px_len(Axis::X).to_f32()); + Size::px(Vec2::new(self.len, self.len)) + } + + fn size_hint(&self, _: Axis) -> Option { + Some(LayoutLen::px(self.len)) + } +} + +/// A rule wins over what the widget says about itself, and a share is a rule: +/// it is a length only to whoever divides one, and nobody here does, so the +/// widget is asked in the whole box rather than in the size it asked for. +#[test] +fn a_share_rule_beats_the_widgets_own_pixel_size() { + let mut h = Harness::new((400, 200)); + let asked = Rc::new(Cell::new(0.0)); + let natural = NaturalSize { + len: 50.0, + asked: asked.clone(), + } + .add(&mut h.rsc); + h.set_root(natural.wrapper()); + assert_eq!(asked.get(), 50.0, "its hint gives it its own size"); + + h.set_len(natural, Axis::X, LayoutLen::LEFTOVER); + h.frame(); + + assert_eq!(asked.get(), 400.0, "the share is all of the box"); +} + #[test] fn a_child_drawn_twice_moves_once() { let mut h = Harness::new((400, 200)); diff --git a/tests/cases/plan.rs b/tests/cases/plan.rs index 031d244..3428786 100644 --- a/tests/cases/plan.rs +++ b/tests/cases/plan.rs @@ -25,11 +25,27 @@ fn some_edits(seed: u64, of: &Plan) -> Edits { Edits { sizes: pick(sized, &mut rng) .into_iter() - .map(|i| (i, [Some(LayoutLen::LEFTOVER), None])) + .map(|i| { + ( + i, + SizeRules { + x: SizeRule::Exact(LayoutLen::LEFTOVER), + y: SizeRule::Free, + }, + ) + }) .collect(), aligns: pick(aligned, &mut rng) .into_iter() - .map(|i| (i, [Some(AxisAlign::POS), None])) + .map(|i| { + ( + i, + Align { + x: Some(AxisAlign::POS), + y: None, + }, + ) + }) .collect(), nodes: pick(nodes, &mut rng) .into_iter() diff --git a/tests/cases/retained.rs b/tests/cases/retained.rs index 5393ea9..9867ecd 100644 --- a/tests/cases/retained.rs +++ b/tests/cases/retained.rs @@ -67,7 +67,7 @@ fn a_redrawn_layered_widget_keeps_the_layer_it_was_entered_on() { let root = Layered { children }.add(&mut h.rsc); h.set_root(root); - h.rsc.widgets_mut().get_dyn_mut(root.id()); + h.rsc.widgets_mut().mark_for_redraw(root.id()); h.frame(); let label = h.rsc.widgets().label(root.id()); @@ -176,9 +176,9 @@ fn a_repaint_that_keeps_its_size_does_not_relay_out() { h.set_root((first, second).span(Dir::RIGHT)); let settled = draws.get(); - // Taking mutable access is the ordinary content-change signal. This - // widget returns the same size, so the parent has nothing to lay out. - let _ = h.rsc.widgets_mut().get_dyn_mut(first.id()); + // Marked with nothing about it changed, and it reports the same size + // either way, so the parent has nothing to lay out. + h.rsc.widgets_mut().mark_for_redraw(first.id()); h.frame(); assert_eq!(draws.get(), settled + 1); @@ -193,7 +193,7 @@ fn a_span_child_survives_the_next_frame() { let bottom = rect(Color::BLUE).height(120).add(&mut h.rsc); h.set_root((top, bottom).span(Dir::DOWN)); - h.rsc.widgets_mut().get_dyn_mut(top.id()); + h.rsc.widgets_mut().mark_for_redraw(top.id()); h.frame(); assert_corners!(h, top, (0, 0), (400, 80)); @@ -655,7 +655,7 @@ fn a_masked_widget_redrawn_on_its_own_sets_its_mask_again() { let masked = inner.masked().add(&mut h.rsc); let other = rect(Color::RED).width(100).add(&mut h.rsc); h.set_root((other, masked).span(Dir::RIGHT)); - h.rsc.widgets_mut().get_dyn_mut(masked.id()); + h.rsc.widgets_mut().mark_for_redraw(masked.id()); h.frame(); assert_corners!(h, inner, (100, 0), (400, 200)); } @@ -995,7 +995,7 @@ fn glyph_origins_compose_identically_when_drawn_and_when_retained() { assert_eq!(draws.get(), before); let retained = primitive_bounds(&h, text.id()); assert!(!retained.is_empty()); - let _ = h.rsc.widgets_mut().get_dyn_mut(text.id()); + h.rsc.widgets_mut().mark_for_redraw(text.id()); h.frame(); assert!(draws.get() > before); assert_eq!(retained, primitive_bounds(&h, text.id())); @@ -1370,7 +1370,7 @@ fn a_redrawn_mask_keeps_reused_primitives_clipped_when_it_moves() { h.set_root((first, masked).span(Dir::DOWN)); let mask = h.render.active[&masked.id()].mask; let settled = draws.get(); - h.rsc.widgets_mut().get_dyn_mut(masked.id()); + h.rsc.widgets_mut().mark_for_redraw(masked.id()); h.frame(); assert_eq!(primitive_masks(&h, inner.id()), vec![mask]); assert_eq!(draws.get(), settled, "a mask repaint must reuse its child"); diff --git a/tests/cases/scroll.rs b/tests/cases/scroll.rs index 149774a..0b7b962 100644 --- a/tests/cases/scroll.rs +++ b/tests/cases/scroll.rs @@ -113,7 +113,7 @@ fn wrapping_content_beside_a_fixed_length_is_stable_warm_and_cold() { let mut warm = Harness::new((900, 300)); let (text, content) = plant(&mut warm); - warm.rsc.widgets_mut().get_dyn_mut(text); + warm.rsc.widgets_mut().mark_for_redraw(text); warm.frame(); let mut cold = Harness::new((900, 300)); diff --git a/tests/cases/unsettled.rs b/tests/cases/unsettled.rs index 7c0f51b..80043ad 100644 --- a/tests/cases/unsettled.rs +++ b/tests/cases/unsettled.rs @@ -10,12 +10,20 @@ //! that node was given. The last is a wrapping text handed back the width //! it measured, rounded to a step below the line it measured there. +use std::collections::HashSet; + use iris::harness::Harness; use iris::prelude::*; use iris::random::Branch; /// Every widget in the same place warm as cold, reported all at once: which /// of a dozen boxes moved is the whole of what a shrunk case has to say. +/// +/// A list that names one widget twice is an error rather than a redundant +/// check. `width`, `sized` and `align` give back the widget they were handed, +/// so a fixture built through them can name one text three times, and then a +/// case comparing six boxes compares four and says nothing about it. One +/// fixture builds both lists, so checking the warm one checks both. #[track_caller] fn assert_same_regions( warm: &Harness, @@ -23,6 +31,17 @@ fn assert_same_regions( cold: &Harness, cold_ids: &[WidgetId], ) { + assert_eq!( + warm_ids.len(), + cold_ids.len(), + "the warm and cold fixtures list different widgets" + ); + let named: HashSet<&WidgetId> = warm_ids.iter().collect(); + assert_eq!( + named.len(), + warm_ids.len(), + "a widget is listed twice: {warm_ids:?}" + ); let mut wrong = Vec::new(); for (i, (&w, &c)) in warm_ids.iter().zip(cold_ids).enumerate() { let (got, want) = (warm.region(&w), cold.region(&c)); @@ -95,7 +114,7 @@ fn repainting_a_stack_uses_the_box_its_sizing_child_decided() { let mut warm = Harness::new((900, 1200)); let ids = plant_stack_in_its_sizing_childs_box(&mut warm); for &id in &ids { - warm.rsc.widgets_mut().get_dyn_mut(id); + warm.rsc.widgets_mut().mark_for_redraw(id); } warm.frame(); @@ -228,7 +247,7 @@ fn one_frame_is_enough() { let first = h.region(&ids[1]).unwrap(); for _ in 0..3 { for &id in &ids { - h.rsc.widgets_mut().get_dyn_mut(id); + h.rsc.widgets_mut().mark_for_redraw(id); } h.frame(); } @@ -250,7 +269,7 @@ fn repainting_everything_moves_nothing() { let mut warm = Harness::new((640, 900)); let ids = plant(&mut warm); for &id in &ids { - warm.rsc.widgets_mut().get_dyn_mut(id); + warm.rsc.widgets_mut().mark_for_redraw(id); } warm.frame(); @@ -562,7 +581,7 @@ fn plant_nested_scrolls(h: &mut Harness) -> Vec { fn redrawing_one_widget_does_not_move_what_scrolls_around_it() { let mut warm = Harness::new((900, 1200)); let ids = plant_nested_scrolls(&mut warm); - warm.rsc.widgets_mut().get_dyn_mut(ids[0]); + warm.rsc.widgets_mut().mark_for_redraw(ids[0]); warm.frame(); let mut cold = Harness::new((900, 1200)); diff --git a/tests/layout_diagnostics.rs b/tests/layout_diagnostics.rs index f7b2293..7c9890a 100644 --- a/tests/layout_diagnostics.rs +++ b/tests/layout_diagnostics.rs @@ -61,8 +61,8 @@ fn a_selected_widget_retains_its_layout_events() { diagnostics::trace_widget(leaf.id()); let _ = diagnostics::take(); - let _ = harness.rsc.widgets_mut().get_dyn_mut(root.id()); - let _ = harness.rsc.widgets_mut().get_dyn_mut(leaf.id()); + harness.rsc.widgets_mut().mark_for_redraw(root.id()); + harness.rsc.widgets_mut().mark_for_redraw(leaf.id()); harness.frame(); let report = diagnostics::take(); @@ -226,7 +226,7 @@ fn layout_cost() { trace_selected(&tree); let leaf = tree.ids[0]; run("repaint", frames, &mut harness, move |harness, _| { - let _ = harness.rsc.widgets_mut().get_dyn_mut(leaf); + harness.rsc.widgets_mut().mark_for_redraw(leaf); }); } @@ -241,7 +241,7 @@ fn layout_cost() { println!("marking {} of {} widgets", dirty.len(), tree.ids.len()); run("many", frames, &mut harness, move |harness, _| { for &id in &dirty { - harness.rsc.widgets_mut().get_dyn_mut(id); + harness.rsc.widgets_mut().mark_for_redraw(id); } }); } diff --git a/tests/revision_cost.rs b/tests/revision_cost.rs index 8f4a385..413e19c 100644 --- a/tests/revision_cost.rs +++ b/tests/revision_cost.rs @@ -194,7 +194,9 @@ fn text_memory() { h.frame(); } report("after 40 resizes"); - // Settled: the output holds still and one leaf repaints per frame. + // Settled: the output holds still and one leaf repaints per frame. Marked + // by taking it mutably because the revision at the top of this file has no + // `mark_for_redraw`, and the same source has to build against both. for _ in 0..10 { let _ = h.rsc.widgets_mut().get_dyn_mut(paragraphs[0]); h.frame(); diff --git a/tests/scenario/mod.rs b/tests/scenario/mod.rs index b7aa7be..0b7b745 100644 --- a/tests/scenario/mod.rs +++ b/tests/scenario/mod.rs @@ -13,7 +13,7 @@ use iris::harness::Harness; use iris::prelude::*; -use iris::random::{Aligns, Edits, Kind, Lens, Plan, Rng, SpanEdit, Tree, build}; +use iris::random::{Edits, Kind, Plan, Rng, SpanEdit, Tree, build}; use std::collections::HashMap; /// A seed per thread but one, since a seed grows, lays out and drops its tree @@ -190,7 +190,7 @@ impl Case { fn mark(warm: &mut Harness, tree: &Tree, step: usize) { for &id in tree.ids.iter().step_by(step) { - warm.rsc.widgets_mut().get_dyn_mut(id); + warm.rsc.widgets_mut().mark_for_redraw(id); } } @@ -198,27 +198,32 @@ fn a_len(rng: &mut Rng) -> Option { Some(LayoutLen::px(20.0 + rng.below(180) as f32)) } -fn resize_one(warm: &mut Harness, tree: &Tree, idx: usize, rng: &mut Rng) -> Lens { - let lens = [a_len(rng), a_len(rng)]; +fn resize_one(warm: &mut Harness, tree: &Tree, idx: usize, rng: &mut Rng) -> SizeRules { + let lens = SizeRules { + x: a_len(rng).into(), + y: a_len(rng).into(), + }; warm.rsc .widgets_mut() - .set_size_rules(tree.sized[idx], lens[0], lens[1]); + .set_size_rules(tree.sized[idx], lens.x, lens.y); lens } -fn realign_one(warm: &mut Harness, tree: &Tree, idx: usize, rng: &mut Rng) -> Aligns { +fn realign_one(warm: &mut Harness, tree: &Tree, idx: usize, rng: &mut Rng) -> Align { let side = |rng: &mut Rng| match rng.below(4) { 0 => None, 1 => Some(AxisAlign::NEG), 2 => Some(AxisAlign::CENTER), _ => Some(AxisAlign::POS), }; - let align = [side(rng), side(rng)]; + let align = Align { + x: side(rng), + y: side(rng), + }; let id = tree.aligned[idx]; - for (axis, align) in [Axis::X, Axis::Y].into_iter().zip(align) { - warm.rsc - .widgets_mut() - .set_alignment(id, axis, align.unwrap_or_default()); + let taken = RegionAlign::from(align); + for axis in Axis::BOTH { + warm.rsc.widgets_mut().set_alignment(id, axis, taken[axis]); } align } diff --git a/tests/trace_unsettled.rs b/tests/trace_unsettled.rs index 7519f2c..af26254 100644 --- a/tests/trace_unsettled.rs +++ b/tests/trace_unsettled.rs @@ -76,7 +76,7 @@ fn what_box_the_text_is_drawn_in() { for _ in 0..2 { for &id in &ids { - h.rsc.widgets_mut().get_dyn_mut(id); + h.rsc.widgets_mut().mark_for_redraw(id); } let _ = diag::take(); h.frame();