diff --git a/benches/message_list.rs b/benches/message_list.rs index 2b3295f..b3cff62 100644 --- a/benches/message_list.rs +++ b/benches/message_list.rs @@ -2,14 +2,14 @@ use iris::prelude::*; use std::time::Instant; struct BenchRsc { - ui: UiData, + ui: Ui, } impl UiRsc for BenchRsc { - fn ui(&self) -> &UiData { + fn ui(&self) -> &Ui { &self.ui } - fn ui_mut(&mut self) -> &mut UiData { + fn ui_mut(&mut self) -> &mut Ui { &mut self.ui } } @@ -60,9 +60,7 @@ fn report(label: &str, elapsed: std::time::Duration, draws: u64, rewrites: u64, } fn bench_first_frame(n: usize) { - let mut rsc = BenchRsc { - ui: UiData::default(), - }; + let mut rsc = BenchRsc { ui: Ui::default() }; let (_list, root) = build_message_list(&mut rsc, n, 20); let mut render = UiRenderState::new(); render.resize((1080.0, 2000.0)); @@ -81,9 +79,7 @@ fn bench_first_frame(n: usize) { } fn bench_scroll(n: usize, ticks: usize) { - let mut rsc = BenchRsc { - ui: UiData::default(), - }; + let mut rsc = BenchRsc { ui: Ui::default() }; let (scroll, root) = build_message_list(&mut rsc, n, 20); let mut render = UiRenderState::new(); render.resize((1080.0, 2000.0)); @@ -120,9 +116,7 @@ fn bench_scroll(n: usize, ticks: usize) { } fn bench_input_grows(n: usize, lines: usize) { - let mut rsc = BenchRsc { - ui: UiData::default(), - }; + let mut rsc = BenchRsc { ui: Ui::default() }; let (scroll, list_root) = build_message_list(&mut rsc, n, 20); let list_area = rsc.ui.widgets.add_strong(Sized { inner: list_root, @@ -183,9 +177,7 @@ fn bench_input_grows(n: usize, lines: usize) { } fn bench_insert_above_anchor(n: usize, inserts: usize) { - let mut rsc = BenchRsc { - ui: UiData::default(), - }; + let mut rsc = BenchRsc { ui: Ui::default() }; let (list, root) = build_message_list(&mut rsc, n, 20); let mut render = UiRenderState::new(); render.resize((1080.0, 2000.0)); @@ -230,9 +222,7 @@ fn bench_insert_above_anchor(n: usize, inserts: usize) { } fn bench_expand_holds_edge(n: usize, growths: usize) { - let mut rsc = BenchRsc { - ui: UiData::default(), - }; + let mut rsc = BenchRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); let growable_index = n.saturating_sub(3); let mut growable = None; @@ -302,9 +292,7 @@ fn bench_expand_holds_edge(n: usize, growths: usize) { } fn bench_redraw_big_text(chars: usize, redraws: usize) { - let mut rsc = BenchRsc { - ui: UiData::default(), - }; + let mut rsc = BenchRsc { ui: Ui::default() }; let content: String = (0..chars) .map(|i| char::from(b'a' + (i % 26) as u8)) .collect(); diff --git a/core/src/event/controller.rs b/core/src/event/controller.rs index 0b9f19e..1c1ece7 100644 --- a/core/src/event/controller.rs +++ b/core/src/event/controller.rs @@ -1,5 +1,5 @@ use crate::{ - ActiveData, WidgetId, + UiRenderState, WidgetId, util::{HashMap, HashSet}, }; use std::any::{Any, TypeId}; @@ -56,7 +56,6 @@ pub trait Controller: ControllerValue { pub struct ControllerManager { by_widget: HashMap>>>, - parents: HashMap>, borrowed: HashSet, removed_while_borrowed: HashSet, command_target: Option, @@ -68,7 +67,6 @@ impl Default for ControllerManager { fn default() -> Self { Self { by_widget: Default::default(), - parents: Default::default(), borrowed: Default::default(), removed_while_borrowed: Default::default(), command_target: None, @@ -111,7 +109,11 @@ impl ControllerManager { .then_some(ControllerId { host, kind }) } - pub fn nearest_id>(&self, mut origin: WidgetId) -> Option { + pub fn nearest_id>( + &self, + mut origin: WidgetId, + render_state: &UiRenderState, + ) -> Option { let kind = TypeId::of::(); loop { let candidate = ControllerId { host: origin, kind }; @@ -122,13 +124,14 @@ impl ControllerManager { if let Some(id) = self.id::(origin) { return Some(id); } - origin = self.parents.get(&origin).copied().flatten()?; + origin = render_state.active.get(&origin)?.parent?; } } pub fn path_to>( &self, mut origin: WidgetId, + render_state: &UiRenderState, ) -> Option<(ControllerId, Vec)> { let mut path = Vec::new(); loop { @@ -136,18 +139,10 @@ impl ControllerManager { if let Some(id) = self.id::(origin) { return Some((id, path)); } - origin = self.parents.get(&origin).copied().flatten()?; + origin = render_state.active.get(&origin)?.parent?; } } - pub fn draw(&mut self, active: &ActiveData) { - self.parents.insert(active.id, active.parent); - } - - pub fn undraw(&mut self, active: &ActiveData) { - self.parents.remove(&active.id); - } - pub fn take>(&mut self, id: ControllerId) -> Option { if id.kind != TypeId::of::() { return None; @@ -232,9 +227,18 @@ impl ControllerManager { self.command_boundary } - pub(crate) fn is_below(&self, mut widget: WidgetId, ancestor: WidgetId) -> bool { + pub(crate) fn is_below( + &self, + mut widget: WidgetId, + ancestor: WidgetId, + render_state: &UiRenderState, + ) -> bool { loop { - let Some(parent) = self.parents.get(&widget).copied().flatten() else { + let Some(parent) = render_state + .active + .get(&widget) + .and_then(|active| active.parent) + else { return false; }; if parent == ancestor { @@ -250,7 +254,6 @@ impl ControllerManager { pub fn remove(&mut self, host: WidgetId) { self.by_widget.remove(&host); - self.parents.remove(&host); if self.borrowed.iter().any(|id| id.host == host) { self.removed_while_borrowed.insert(host); } diff --git a/core/src/event/manager.rs b/core/src/event/manager.rs index 71b944b..d62205c 100644 --- a/core/src/event/manager.rs +++ b/core/src/event/manager.rs @@ -60,14 +60,12 @@ impl EventsLike for EventManager { } fn draw(&mut self, active: &ActiveData) { - self.controllers.draw(active); for t in self.widget_to_types.get(&active.id).into_flat_iter() { self.types.get_mut(t).unwrap().draw(active); } } fn undraw(&mut self, active: &ActiveData) { - self.controllers.undraw(active); for t in self.widget_to_types.get(&active.id).into_flat_iter() { self.types.get_mut(t).unwrap().undraw(active); } diff --git a/core/src/event/rsc.rs b/core/src/event/rsc.rs index 2a72997..9fbbfa7 100644 --- a/core/src/event/rsc.rs +++ b/core/src/event/rsc.rs @@ -44,7 +44,11 @@ pub trait HasEvents: Sized + UiRsc + HasState { origin: impl IdLike, f: impl FnOnce(ControllerId, &mut C, &mut Self) -> T, ) -> Option { - let id = self.events().controllers.nearest_id::(origin.id())?; + let render_handle = self.ui().render_state(); + let id = self + .events() + .controllers + .nearest_id::(origin.id(), &render_handle.get())?; self.with_controller(id, |controller, rsc| f(id, controller, rsc)) } @@ -54,14 +58,22 @@ pub trait HasEvents: Sized + UiRsc + HasState { fn run_command(&mut self, command: Command) -> CommandResult { let revision = self.events().controllers.command_target_revision(); - if let Some(boundary) = self.events().controllers.command_boundary() - && self - .events() - .controllers - .command_target() - .is_none_or(|target| !self.events().controllers.is_below(target.host(), boundary)) - { - return CommandResult::Unused; + if let Some(boundary) = self.events().controllers.command_boundary() { + let render_handle = self.ui().render_state(); + let outside_boundary = + self.events() + .controllers + .command_target() + .is_none_or(|target| { + !self.events().controllers.is_below( + target.host(), + boundary, + &render_handle.get(), + ) + }); + if outside_boundary { + return CommandResult::Unused; + } } let Some((id, mut controller)) = self.events_mut().controllers.take_command_target() else { return CommandResult::Unused; @@ -96,6 +108,12 @@ pub trait RunEvents: HasEvents { data: ::Data<'_>, state: &mut Self::State, ) { + // Keep the last completed frame read-locked for the whole callback. + // Rsc methods may take further shared reads through `render_state`, + // while any attempt to start a render from an event fails at the + // mutable-borrow boundary instead of exposing an in-progress tree. + let render_handle = self.ui().render_state(); + let _render_state = render_handle.get(); let f = self.events_mut().get_type::().run_fn(id); f(EventCtx { state, data }, self) } diff --git a/core/src/render/mod.rs b/core/src/render/mod.rs index bd4f130..35d8c4c 100644 --- a/core/src/render/mod.rs +++ b/core/src/render/mod.rs @@ -1,5 +1,5 @@ use crate::{ - UiData, UiRenderState, + Ui, UiData, render::{ data::{PrimitiveInstance, instance_slot_layout}, texture::GpuTextures, @@ -180,13 +180,11 @@ impl UiRenderNode { } } - pub fn update( - &mut self, - device: &Device, - queue: &Queue, - ui: &mut UiData, - ui_render: &mut UiRenderState, - ) -> FrameUpdateStats { + pub fn update(&mut self, device: &Device, queue: &Queue, ui: &mut Ui) -> FrameUpdateStats { + let render_handle = ui.render_state.clone(); + let mut render_guard = render_handle.get_mut(); + let ui_render = &mut *render_guard; + let ui_data: &mut UiData = ui; self.active.clear(); for (i, order) in ui_render.layers.iter_mut() { self.active.push(i); @@ -233,11 +231,11 @@ impl UiRenderNode { } else { false }; - let (entries, dirty) = ui.masks.for_upload(); + let (entries, dirty) = ui_data.masks.for_upload(); let masks_resized = self.masks.update(device, queue, entries, dirty); - let (entries, dirty) = ui.move_offsets.for_upload(); + let (entries, dirty) = ui_data.move_offsets.for_upload(); let moves_resized = self.move_offsets.update(device, queue, entries, dirty); - let (entries, dirty) = ui.paints.for_upload(); + let (entries, dirty) = ui_data.paints.for_upload(); let paints_resized = self.paints.update(device, queue, entries, dirty); if masks_resized || moves_resized || instances_resized || paints_resized { self.masks_group = Self::masks_group( @@ -249,7 +247,9 @@ impl UiRenderNode { &self.paints, ); } - let rebuild_main = self.textures.update(&mut ui.textures, &self.rsc_layout); + let rebuild_main = self + .textures + .update(&mut ui_data.textures, &self.rsc_layout); if rebuild_main { self.rsc_group = Self::rsc_group(device, &self.rsc_layout, &self.textures); } diff --git a/core/src/ui/mod.rs b/core/src/ui/mod.rs index f4ed28c..c7575ad 100644 --- a/core/src/ui/mod.rs +++ b/core/src/ui/mod.rs @@ -1,6 +1,11 @@ use crate::{ Mask, MoveOffset, Paints, TextData, Textures, WeakWidget, WidgetId, Widgets, util::TrackedArena, }; +use std::{ + cell::{Ref, RefCell, RefMut}, + ops::{Deref, DerefMut}, + rc::Rc, +}; mod access; mod active; @@ -23,6 +28,75 @@ pub struct UiData { animating: Vec, } +#[derive(Clone)] +pub struct RenderHandle { + pub(crate) render_state: Rc>, +} + +impl RenderHandle { + /// The retained result of the last completed frame. The framework holds + /// the corresponding mutable borrow for the whole of a render update, so + /// a read attempted while that state is incomplete fails at the boundary + /// instead of observing half a frame. + pub fn get(&self) -> Ref<'_, UiRenderState> { + self.render_state + .try_borrow() + .expect("render state cannot be read while a frame is being rendered") + } + + pub(crate) fn get_mut(&self) -> RefMut<'_, UiRenderState> { + self.render_state + .try_borrow_mut() + .expect("render state cannot be mutated while it is being read") + } +} + +impl Default for RenderHandle { + fn default() -> Self { + Self { + render_state: Rc::new(RefCell::new(UiRenderState::new())), + } + } +} + +#[derive(Default)] +pub struct Ui { + data: UiData, + pub(crate) render_state: RenderHandle, +} + +impl Ui { + /// A read-only handle to the retained result of the last completed frame. + /// The handle is owned so a caller may keep its read guard while mutating + /// unrelated resources on the `Rsc` that owns this `Ui`. + pub fn render_state(&self) -> RenderHandle { + self.render_state.clone() + } + + pub fn resize(&self, size: impl Into) { + self.render_state.get_mut().resize(size); + } + + pub fn set_density(&mut self, density: f32) { + self.data.text.density = density; + self.render_state.get_mut().set_density(density); + } +} + +impl Deref for Ui { + type Target = UiData; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl DerefMut for Ui { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} + impl UiData { /// Ask for `id`'s [`crate::Widget::tick`] to run every frame until it /// says it is done. Idempotent -- registering an already-animating @@ -48,8 +122,16 @@ impl UiData { } pub trait UiRsc { - fn ui(&self) -> &UiData; - fn ui_mut(&mut self) -> &mut UiData; + fn ui(&self) -> &Ui; + fn ui_mut(&mut self) -> &mut Ui; + + fn draw<'a>(&mut self, root: impl Into>) + where + Self: Sized, + { + let render_state = self.ui().render_state.clone(); + render_state.get_mut().update(root, self); + } #[allow(unused_variables)] fn on_add(&mut self, id: WeakWidget) {} diff --git a/core/src/ui/painter.rs b/core/src/ui/painter.rs index e344ac8..caaa0af 100644 --- a/core/src/ui/painter.rs +++ b/core/src/ui/painter.rs @@ -1,6 +1,6 @@ use crate::{ Axis, Len, MoveOffset, PaintId, RegionAlign, RenderedText, Size, StrongWidget, TextAttrs, - TextBuffer, TextData, TextureHandle, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, + TextBuffer, TextData, TextureHandle, UiData, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, WidgetId, render::{ Drawn, GlyphPrimitive, IMAGE_BINDING, Mask, MaskIdx, MoveIdx, NOT_DRAWN, Primitive, @@ -11,7 +11,7 @@ use crate::{ }; pub struct Painter<'a> { - pub(super) state: &'a mut UiRenderState, + pub(super) render_state: &'a mut UiRenderState, pub(super) rsc: &'a mut dyn UiRsc, pub(super) region: UiRegion, @@ -46,7 +46,7 @@ impl DrawResult<'_, '_> { if !self.painter.size_dependencies.contains(&self.child) { self.painter.size_dependencies.push(self.child); } - self.painter.state.active[&self.child].size + self.painter.render_state.active[&self.child].size } } @@ -94,10 +94,10 @@ impl<'a> Painter<'a> { }; let h = match self.take_recycled(P::BINDING, drawn) { Some(h) => { - self.state.primitives.recycle(&h, inst); + self.render_state.primitives.recycle(&h, inst); h } - None => self.state.write_primitive(self.layer, drawn, inst), + None => self.render_state.write_primitive(self.layer, drawn, inst), }; if self.mask != MaskIdx::NONE { self.rsc.ui_mut().masks.push_ref(self.mask); @@ -109,7 +109,7 @@ impl<'a> Painter<'a> { /// Take ownership of a handle this widget just wrote. fn own(&mut self, h: PrimitiveHandle) { - self.state + self.render_state .primitives .set_handle_index(h.slot, self.primitives.len() as u32); self.primitives.push(h); @@ -156,7 +156,7 @@ impl<'a> Painter<'a> { /// with no radius argument anywhere that could fall out of step with /// the one being drawn. pub fn set_mask_to_widget(&mut self, shape: &StrongWidget) { - let slot = self.state.first_primitive(shape.id()).unwrap_or_else(|| { + let slot = self.render_state.first_primitive(shape.id()).unwrap_or_else(|| { panic!( "'{}' was given as a mask's shape but drew no primitive, so there is nothing to \ clip to", @@ -172,7 +172,7 @@ impl<'a> Painter<'a> { "set_mask called twice while drawing one widget: the second would replace the first \ rather than nest inside it", ); - let binding = self.state.primitives.instance(shape).binding; + let binding = self.render_state.primitives.instance(shape).binding; assert_eq!( binding, RectPrimitive::BINDING, @@ -248,7 +248,7 @@ impl<'a> Painter<'a> { let next = [offset.x, offset.y]; if self.rsc.ui().move_offsets[slot.idx()].delta != next { self.rsc.ui_mut().move_offsets.get_mut(slot).delta = next; - self.state.note_move(); + self.render_state.note_move(); } } @@ -262,7 +262,10 @@ impl<'a> Painter<'a> { { None } else { - self.state.active.get(&id.id()).map(|a| a.size.axis(axis)) + self.render_state + .active + .get(&id.id()) + .map(|a| a.size.axis(axis)) }; if len.is_some() && !self.size_dependencies.contains(&id.id()) { self.size_dependencies.push(id.id()); @@ -277,7 +280,7 @@ impl<'a> Painter<'a> { ) -> DrawResult<'p, 'a> { self.children.push(id.id()); let parent_move_slot = self.child_move_slot.unwrap_or(self.move_slot); - self.state.draw_inner( + self.render_state.draw_inner( self.layer, id.id(), region, @@ -300,16 +303,16 @@ impl<'a> Painter<'a> { ) -> DrawResult<'p, 'a> { let region = region.within(&self.region); let retained = self - .state + .render_state .active .get(&id.id()) .map(|active| (active.layer, active.mask)); - if self.state.place(id.id(), region, self.rsc).is_some() { + if self.render_state.place(id.id(), region, self.rsc).is_some() { } else if let Some((layer, mask)) = retained { self.children.push(id.id()); self.rsc.widgets_mut().needs_redraw.insert(id.id()); let parent_move_slot = self.child_move_slot.unwrap_or(self.move_slot); - self.state.draw_inner( + self.render_state.draw_inner( layer, id.id(), region, @@ -322,7 +325,7 @@ impl<'a> Painter<'a> { } else { self.children.push(id.id()); let parent_move_slot = self.child_move_slot.unwrap_or(self.move_slot); - self.state.draw_inner( + self.render_state.draw_inner( self.layer, id.id(), region, @@ -385,7 +388,7 @@ impl<'a> Painter<'a> { fn write_image(&mut self, texture_idx: u32, region: UiRegion) { let h = match self.take_recycled(IMAGE_BINDING, Drawn::Yes) { Some(h) => { - self.state.primitives.recycle_image( + self.render_state.primitives.recycle_image( &h, self.id, texture_idx, @@ -395,7 +398,7 @@ impl<'a> Painter<'a> { ); h } - None => self.state.write_image( + None => self.render_state.write_image( self.layer, self.id, texture_idx, @@ -416,9 +419,9 @@ impl<'a> Painter<'a> { attrs: &TextAttrs, width: Option, ) -> RenderedText { - let density = self.state.density; - self.state.shape_count += 1; - let ui = self.rsc.ui_mut(); + let density = self.render_state.density; + self.render_state.shape_count += 1; + let ui: &mut UiData = self.rsc.ui_mut(); ui.text .render(buffer, attrs, width, &mut ui.textures, density) } @@ -477,17 +480,17 @@ impl<'a> Painter<'a> { } pub fn output_size(&self) -> Vec2 { - self.state.output_size + self.render_state.output_size } /// Physical pixels per `dp` -- see `UiRenderState::density`'s field /// doc. What `Len::dp`'s `apply_rest` call resolves against. pub fn density(&self) -> f32 { - self.state.density + self.render_state.density } pub fn px_size(&mut self) -> Vec2 { - self.region.size().to_abs(self.state.output_size) + self.region.size().to_abs(self.render_state.output_size) } pub fn text_data(&mut self) -> &mut TextData { @@ -495,11 +498,11 @@ impl<'a> Painter<'a> { } pub fn child_layer(&mut self) { - self.layer = self.state.layers.child(self.layer); + self.layer = self.render_state.layers.child(self.layer); } pub fn next_layer(&mut self) { - self.layer = self.state.layers.next(self.layer); + self.layer = self.render_state.layers.next(self.layer); } pub fn label(&self) -> &str { diff --git a/core/src/ui/render_state.rs b/core/src/ui/render_state.rs index d5e243d..6bb0a53 100644 --- a/core/src/ui/render_state.rs +++ b/core/src/ui/render_state.rs @@ -441,7 +441,7 @@ impl UiRenderState { ] }); let mut painter = Painter { - state: self, + render_state: self, region, mask, move_slot, @@ -466,7 +466,7 @@ impl UiRenderState { widget.size_hint(Axis::X).map(|len| len.fold_dp(density)), widget.size_hint(Axis::Y).map(|len| len.fold_dp(density)), ]; - painter.state.draw_count += 1; + painter.render_state.draw_count += 1; widget.draw(&mut painter); let size = painter.size.unwrap_or_else(|| { panic!( @@ -491,10 +491,10 @@ impl UiRenderState { ); } drop(widget); - painter.state.draw_started.remove(&id); + painter.render_state.draw_started.remove(&id); let Painter { - state: _, + render_state: _, rsc: _, region, mask: _, diff --git a/examples/bench_images.rs b/examples/bench_images.rs index e08a670..71d63a0 100644 --- a/examples/bench_images.rs +++ b/examples/bench_images.rs @@ -40,12 +40,7 @@ impl DefaultAppState for State { } } - fn window_event( - &mut self, - event: winit::event::WindowEvent, - rsc: &mut DefaultRsc, - _render: &mut UiRenderState, - ) { + fn window_event(&mut self, event: winit::event::WindowEvent, rsc: &mut DefaultRsc) { if !matches!(event, winit::event::WindowEvent::RedrawRequested) { return; } diff --git a/examples/tabs/main.rs b/examples/tabs/main.rs index 5b2058b..d4d4ec9 100644 --- a/examples/tabs/main.rs +++ b/examples/tabs/main.rs @@ -24,16 +24,12 @@ impl DefaultAppState for Client { } } - fn window_event( - &mut self, - _: WindowEvent, - rsc: &mut DefaultRsc, - render: &mut UiRenderState, - ) { + fn window_event(&mut self, _: WindowEvent, rsc: &mut DefaultRsc) { + let render_state = rsc.ui.render_state(); let new = format!( "widgets: {}\nactive: {}\nviews: {}", rsc.widgets().len(), - render.active_widgets(), + render_state.get().active_widgets(), self.ui_state.renderer.ui.view_count(), ); if new != *rsc.widgets()[self.info].content { diff --git a/src/access_tests.rs b/src/access_tests.rs index 1bd128b..bbdcad9 100644 --- a/src/access_tests.rs +++ b/src/access_tests.rs @@ -3,9 +3,7 @@ use crate::prelude::*; #[test] fn a_named_widget_reaches_the_tree_with_its_role_and_bounds() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let leaf: WeakWidget = rect(PaintId::WHITE).label("Add task").add(&mut rsc); let root = leaf.upgrade(&mut rsc).any(); let mut render = UiRenderState::new(); @@ -37,9 +35,7 @@ fn a_named_widget_reaches_the_tree_with_its_role_and_bounds() { #[test] fn a_widget_with_no_label_never_reaches_the_tree() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let root = rsc.ui.widgets.add_strong(rect(PaintId::WHITE)); let mut render = UiRenderState::new(); render.resize((800.0, 600.0)); @@ -55,9 +51,7 @@ fn a_widget_with_no_label_never_reaches_the_tree() { #[test] fn bounds_follow_a_moved_widget_and_updates_stay_incremental() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let leaf: WeakWidget = rect(PaintId::WHITE).label("thing").add(&mut rsc); let leaf_strong = leaf.upgrade(&mut rsc).any(); let offset = rsc.ui.widgets.add_strong(Offset { diff --git a/src/android/render.rs b/src/android/render.rs index 0aba573..f1c4e62 100644 --- a/src/android/render.rs +++ b/src/android/render.rs @@ -4,7 +4,7 @@ use android_view::{ jni::{JavaVM, objects::GlobalRef}, ndk::native_window::NativeWindow, }; -use iris_core::{FrameParts, LinearRgba, UiData, UiRenderNode, UiRenderState}; +use iris_core::{FrameParts, LinearRgba, Ui, UiRenderNode}; use pollster::FutureExt; use std::time::Instant; use wgpu::{ @@ -330,10 +330,10 @@ impl AndroidRenderer { }) } - pub fn update(&mut self, ui: &mut UiData, render: &mut UiRenderState) -> FrameDiagnostics { + pub fn update(&mut self, ui: &mut Ui) -> FrameDiagnostics { let atlas_pages_grown_prev = self.ui.take_atlas_pages_grown(); let image_bind_group_creates_prev = self.ui.take_image_bind_group_creates(); - let stats = self.ui.update(&self.device, &self.queue, ui, render); + let stats = self.ui.update(&self.device, &self.queue, ui); self.frame_count += 1; FrameDiagnostics { masks_resized: stats.masks_resized, diff --git a/src/android/view.rs b/src/android/view.rs index 7328c7d..9373576 100644 --- a/src/android/view.rs +++ b/src/android/view.rs @@ -123,7 +123,7 @@ pub trait HasAndroidUiState: Sized + 'static { pub trait AndroidAppState: HasAndroidUiState { fn new(ui_state: AndroidUiState, rsc: &mut AndroidRsc) -> Self; #[allow(unused_variables)] - fn back_pressed(&mut self, rsc: &mut AndroidRsc, render: &mut UiRenderState) -> bool { + fn back_pressed(&mut self, rsc: &mut AndroidRsc) -> bool { false } #[allow(unused_variables)] @@ -167,7 +167,7 @@ impl WindowInsets { } pub struct AndroidRsc { - pub ui: UiData, + pub ui: Ui, pub events: EventManager, pub tasks: Tasks, pub state: WidgetState, @@ -181,10 +181,10 @@ impl AndroidRsc { } impl UiRsc for AndroidRsc { - fn ui(&self) -> &UiData { + fn ui(&self) -> &Ui { &self.ui } - fn ui_mut(&mut self) -> &mut UiData { + fn ui_mut(&mut self) -> &mut Ui { &mut self.ui } fn on_draw(&mut self, active: &ActiveData) { @@ -232,7 +232,6 @@ impl HasWidgetState for AndroidRsc { /// Java the same way android-view's own demo does. pub struct IrisViewPeer { pub(super) rsc: AndroidRsc, - pub(super) render: UiRenderState, pub(super) state: State, task_recv: TaskMsgReceiver>, /// The one ruler this view dates everything on: touch samples in @@ -277,7 +276,9 @@ impl IrisViewPeer { let ui_state = self.state.android_state_mut(); let cursor = ui_state.cursor.clone(); let old_focus = ui_state.focus; - self.render + let render_state = self.rsc.ui.render_state(); + render_state + .get() .run_sensors(&mut self.rsc, &mut self.state, cursor, window_size); let ui_state = self.state.android_state_mut(); @@ -306,7 +307,11 @@ impl IrisViewPeer { let ui_state = self.state.android_state_mut(); ui_state.cursor.end_frame(); - if self.render.needs_redraw(&ui_state.root, self.rsc.widgets()) { + let render_state = self.rsc.ui.render_state(); + if render_state + .get() + .needs_redraw(&ui_state.root, self.rsc.widgets()) + { ctx.view.post_frame_callback(&mut ctx.env); } } @@ -360,11 +365,17 @@ impl IrisViewPeer { "render(): root={:?} widgets={} active={} root_px={:?} out_size={:?}", ui_state.root.is_some(), self.rsc.widgets().len(), - self.render.active_widgets(), + self.rsc.ui.render_state().get().active_widgets(), ui_state .root .as_ref() - .and_then(|r| self.render.window_region(r, &self.rsc)), + .and_then(|r| { + self.rsc + .ui + .render_state() + .get() + .window_region(r, &self.rsc) + }), self.window_size(), ); } @@ -374,12 +385,12 @@ impl IrisViewPeer { ctx.view.post_frame_callback(&mut ctx.env); } let ui_state = self.state.android_state_mut(); - self.render.update(&ui_state.root, &mut self.rsc); + self.rsc.draw(&ui_state.root); let ui_state = self.state.android_state_mut(); let Some(renderer) = &mut ui_state.renderer else { return; }; - let frame_diagnostics = renderer.update(&mut self.rsc.ui, &mut self.render); + let frame_diagnostics = renderer.update(&mut self.rsc.ui); if renderer.frame_count() <= DIAGNOSTIC_FRAMES { log::info!( "iris frame diagnostics: frame={} masks_resized={} moves_resized={} \ @@ -400,26 +411,33 @@ impl IrisViewPeer { .android_state_mut() .frame_report .record(now, parts, animating); - crate::diagnostics::log_frame(&self.render, now, parts, animating); + let render_state = self.rsc.ui.render_state(); + crate::diagnostics::log_frame(&render_state.get(), now, parts, animating); if crate::diagnostics::trace_enabled() { let ui_state = self.state.android_state(); log::debug!( target: "iris::frame", "render(): after update active={} root_px={:?}", - self.render.active_widgets(), + self.rsc.ui.render_state().get().active_widgets(), ui_state .root .as_ref() - .and_then(|r| self.render.window_region(r, &self.rsc)), + .and_then(|r| { + self.rsc + .ui + .render_state() + .get() + .window_region(r, &self.rsc) + }), ); } let ui_state = self.state.android_state_mut(); - if let Some(tree_update) = - ui_state - .access - .update(self.rsc.widgets(), &self.render, &self.rsc) - { + if let Some(tree_update) = ui_state.access.update( + self.rsc.widgets(), + &self.rsc.ui.render_state().get(), + &self.rsc, + ) { let ui_state = self.state.android_state_mut(); if let Some(events) = ui_state.access_adapter.update_if_active(|| tree_update) { ctx.push_dynamic_deferred_callback(move |env, view| { @@ -467,7 +485,7 @@ impl ViewPeer for IrisViewPeer { self.after_input(ctx); return true; } - let handled = self.state.back_pressed(&mut self.rsc, &mut self.render); + let handled = self.state.back_pressed(&mut self.rsc); if handled { self.after_input(ctx); } @@ -646,7 +664,7 @@ impl ViewPeer for IrisViewPeer { // dividing the whole window into a separate logical space, which // is what used to make every `abs`-unit size (a fixed `.height(56)` // in particular) mean something different from a `rest`-based one. - self.render.resize((width as f32, height as f32)); + self.rsc.ui.resize((width as f32, height as f32)); // `AndroidRenderer::resize` only reconfigures the wgpu surface and // rewrites the window uniform -- device, atlas, buffers and bind @@ -779,9 +797,11 @@ impl AccessibilityNodeProvider for IrisViewPeer { ctx: &mut CallbackCtx<'local>, virtual_view_id: jint, ) -> AccessibilityNodeInfo<'local> { + let render_handle = self.rsc.ui.render_state(); + let render_state = render_handle.get(); let mut source = AndroidAccessSource { widgets: self.rsc.widgets(), - render: &self.render, + render: &render_state, rsc: &self.rsc, }; let ui_state = self.state.android_state_mut(); @@ -798,9 +818,11 @@ impl AccessibilityNodeProvider for IrisViewPeer { ctx: &mut CallbackCtx<'local>, focus_type: jint, ) -> AccessibilityNodeInfo<'local> { + let render_handle = self.rsc.ui.render_state(); + let render_state = render_handle.get(); let mut source = AndroidAccessSource { widgets: self.rsc.widgets(), - render: &self.render, + render: &render_state, rsc: &self.rsc, }; let ui_state = self.state.android_state_mut(); @@ -869,18 +891,15 @@ pub fn new_peer<'local, State: AndroidAppState>( state: Default::default(), _state: PhantomData, }; - rsc.ui.text.density = content_scale; + rsc.ui.set_density(content_scale); let shared = Rc::new(RefCell::new(Shared::default())); let ui_state = AndroidUiState::new(shared.clone(), content_scale); let mut state = State::new(ui_state, &mut rsc); let platform_vm = env.get_java_vm().unwrap(); let platform_view = env.new_global_ref(&view.0).unwrap(); state.platform_ready(&mut rsc, platform_vm, platform_view); - let mut render = UiRenderState::new(); - render.set_density(content_scale); let peer = IrisViewPeer { rsc, - render, state, task_recv, device_clock: None, diff --git a/src/default/mod.rs b/src/default/mod.rs index c785efb..765b10e 100644 --- a/src/default/mod.rs +++ b/src/default/mod.rs @@ -99,30 +99,18 @@ pub trait DefaultAppState: HasDefaultUiState { fn new(ui_state: DefaultUiState, rsc: &mut DefaultRsc, proxy: Proxy) -> Self; #[allow(unused_variables)] - fn event( - &mut self, - event: Self::Event, - rsc: &mut DefaultRsc, - render: &mut UiRenderState, - ) { - } + fn event(&mut self, event: Self::Event, rsc: &mut DefaultRsc) {} #[allow(unused_variables)] - fn exit(&mut self, rsc: &mut DefaultRsc, render: &mut UiRenderState) {} + fn exit(&mut self, rsc: &mut DefaultRsc) {} #[allow(unused_variables)] - fn window_event( - &mut self, - event: WindowEvent, - rsc: &mut DefaultRsc, - render: &mut UiRenderState, - ) { - } + fn window_event(&mut self, event: WindowEvent, rsc: &mut DefaultRsc) {} fn window_attributes() -> WindowAttributes { Default::default() } } pub struct DefaultRsc { - pub ui: UiData, + pub ui: Ui, pub events: EventManager, pub tasks: Tasks, pub state: WidgetState, @@ -150,11 +138,11 @@ impl DefaultRsc { } impl UiRsc for DefaultRsc { - fn ui(&self) -> &UiData { + fn ui(&self) -> &Ui { &self.ui } - fn ui_mut(&mut self) -> &mut UiData { + fn ui_mut(&mut self) -> &mut Ui { &mut self.ui } @@ -204,7 +192,6 @@ impl HasWidgetState for DefaultRsc { pub struct DefaultApp { rsc: DefaultRsc, - render: UiRenderState, state: State, task_recv: TaskMsgReceiver>, } @@ -232,26 +219,22 @@ impl AppState for DefaultApp { // `content_scale`. See `iris_core::TextData::density` for why the // shaper keeps its own. let scale = content_scale(default_state.window.as_ref()); - rsc.ui.text.density = scale; + rsc.ui.set_density(scale); let state = State::new(default_state, &mut rsc, proxy); - let mut render = UiRenderState::new(); - render.set_density(scale); Self { rsc, state, - render, task_recv, } } fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) { - self.state.event(event, &mut self.rsc, &mut self.render); + self.state.event(event, &mut self.rsc); } fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) { let Self { rsc, - render, state, task_recv, } = self; @@ -290,7 +273,11 @@ impl AppState for DefaultApp { } else { "move" }; - let t_ms = cursor_state.time.duration_since(render.epoch()).as_millis() as u64; + let render_state = rsc.ui.render_state(); + let t_ms = cursor_state + .time + .duration_since(render_state.get().epoch()) + .as_millis() as u64; crate::sense::log_input_event( action, cursor_state.pos.x, @@ -300,7 +287,10 @@ impl AppState for DefaultApp { ); } let window_size = ui_state.window_size(); - render.run_sensors(rsc, state, cursor_state, window_size); + let render_state = rsc.ui.render_state(); + render_state + .get() + .run_sensors(rsc, state, cursor_state, window_size); } let ui_state = state.default_state_mut(); if old != ui_state.focus @@ -324,23 +314,25 @@ impl AppState for DefaultApp { if animating { ui_state.window.request_redraw(); } - render.update(&ui_state.root, rsc); - ui_state.renderer.update(&mut rsc.ui, render); + rsc.draw(&ui_state.root); + ui_state.renderer.update(&mut rsc.ui); let mut parts = ui_state.renderer.draw(); parts.total = frame_start.elapsed(); - crate::diagnostics::log_frame(render, frame_start, parts, animating); - if let Some(tree_update) = ui_state.access.update(rsc.widgets(), render, rsc) { + let render_state = rsc.ui.render_state(); + let render_state = render_state.get(); + crate::diagnostics::log_frame(&render_state, frame_start, parts, animating); + if let Some(tree_update) = ui_state.access.update(rsc.widgets(), &render_state, rsc) + { ui_state.access_adapter.update_if_active(|| tree_update); } } WindowEvent::Resized(size) => { - render.resize((size.width, size.height)); + rsc.ui.resize((size.width, size.height)); ui_state.renderer.resize(size) } WindowEvent::ScaleFactorChanged { .. } => { let scale = content_scale(ui_state.window.as_ref()); - rsc.ui.text.density = scale; - render.set_density(scale); + rsc.ui.set_density(scale); ui_state.window.request_redraw(); } WindowEvent::KeyboardInput { event, .. } => { @@ -422,16 +414,20 @@ impl AppState for DefaultApp { } _ => (), } - state.window_event(event, rsc, render); + state.window_event(event, rsc); let ui_state = self.state.default_state_mut(); - if render.needs_redraw(&ui_state.root, rsc.widgets()) { + let render_state = rsc.ui.render_state(); + if render_state + .get() + .needs_redraw(&ui_state.root, rsc.widgets()) + { ui_state.renderer.window().request_redraw(); } ui_state.input.end_frame(); } fn exit(&mut self) { - self.state.exit(&mut self.rsc, &mut self.render); + self.state.exit(&mut self.rsc); } } diff --git a/src/default/render.rs b/src/default/render.rs index 9b1fcf8..4fa3103 100644 --- a/src/default/render.rs +++ b/src/default/render.rs @@ -1,5 +1,5 @@ use crate::task::RequestRedraw; -use iris_core::{FrameParts, LinearRgba, UiData, UiRenderNode, UiRenderState, util::Vec2}; +use iris_core::{FrameParts, LinearRgba, Ui, UiRenderNode, util::Vec2}; use pollster::FutureExt; use std::sync::Arc; use std::time::Instant; @@ -26,8 +26,8 @@ pub struct UiRenderer { } impl UiRenderer { - pub fn update(&mut self, ui: &mut UiData, render: &mut UiRenderState) { - self.ui.update(&self.device, &self.queue, ui, render); + pub fn update(&mut self, ui: &mut Ui) { + self.ui.update(&self.device, &self.queue, ui); } /// The two waits, so a desktop frame divides up the same way an diff --git a/src/harness.rs b/src/harness.rs index 7eebb5a..56f4ab1 100644 --- a/src/harness.rs +++ b/src/harness.rs @@ -168,7 +168,7 @@ impl OpenUrl for HarnessState { /// `AndroidRsc` minus the windowing, for the same reason those two are /// separate types (`AndroidRsc`'s own doc). pub struct HarnessRsc { - pub ui: UiData, + pub ui: Ui, pub events: EventManager, pub tasks: Tasks, pub state: WidgetState, @@ -176,10 +176,10 @@ pub struct HarnessRsc { } impl UiRsc for HarnessRsc { - fn ui(&self) -> &UiData { + fn ui(&self) -> &Ui { &self.ui } - fn ui_mut(&mut self) -> &mut UiData { + fn ui_mut(&mut self) -> &mut Ui { &mut self.ui } fn on_draw(&mut self, active: &ActiveData) { @@ -239,7 +239,6 @@ impl> std::ops::IndexMut for HarnessRsc { /// the pointer, all advanced by the caller. See the module doc. pub struct Harness { pub rsc: HarnessRsc, - pub render: UiRenderState, pub state: HarnessState, task_recv: TaskMsgReceiver, redraws: Arc, @@ -258,19 +257,16 @@ impl Harness { let redraws = Arc::new(RedrawCounter::default()); let (tasks, task_recv) = Tasks::init(redraws.clone()); let mut rsc = HarnessRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), tasks, state: WidgetState::default(), _state: PhantomData, }; - rsc.ui.text.density = density; - let mut render = UiRenderState::new(); - render.set_density(density); - render.resize(size); + rsc.ui.set_density(density); + rsc.ui.resize(size); Self { rsc, - render, state: HarnessState::new(), task_recv, redraws, @@ -302,15 +298,16 @@ impl Harness { let now = self.at(t_ms); let at = Instant::now(); let animating = self.rsc.ui.tick_animations(now); - self.render.update(&self.state.root, &mut self.rsc); + self.rsc.draw(&self.state.root); // No GPU here, so there is nothing to acquire and nothing to // submit: the frame is all `build`, which is honest rather than // zero-filled (`FrameParts::whole`). `layout`/`redraw`/ // `primitives` are still real, because `render.update` just ran; // see `iris::diagnostics::log_frame`'s own doc for why this reads // those back rather than timing anything itself. + let render_state = self.rsc.ui.render_state(); crate::diagnostics::log_frame( - &self.render, + &render_state.get(), now, FrameParts::whole(at.elapsed()), animating, @@ -344,7 +341,9 @@ impl Harness { } crate::sense::log_input_event(action.word(), pos.x, pos.y, t_ms, &[]); let cursor = self.cursor.clone(); - self.render + let render_state = self.rsc.ui.render_state(); + render_state + .get() .run_sensors(&mut self.rsc, &mut self.state, cursor, self.size); self.frame(t_ms); self.cursor.end_frame(); diff --git a/src/layout_tests.rs b/src/layout_tests.rs index 4f75006..04e02d7 100644 --- a/src/layout_tests.rs +++ b/src/layout_tests.rs @@ -2,14 +2,14 @@ use crate::prelude::*; use std::{cell::Cell, cell::RefCell, rc::Rc}; pub(crate) struct TestRsc { - pub(crate) ui: UiData, + pub(crate) ui: Ui, } impl UiRsc for TestRsc { - fn ui(&self) -> &UiData { + fn ui(&self) -> &Ui { &self.ui } - fn ui_mut(&mut self) -> &mut UiData { + fn ui_mut(&mut self) -> &mut Ui { &mut self.ui } } @@ -87,9 +87,7 @@ impl Widget for TracedParent { #[test] fn a_widget_retains_its_entry_layer_not_its_child_cursor() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let back = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); let front = rsc.ui.widgets.add_strong(Rect::new(PaintId::RED)); let stack = rsc.ui.widgets.add_strong(Stack { @@ -125,9 +123,7 @@ fn a_widget_retains_its_entry_layer_not_its_child_cursor() { #[test] fn a_size_dependent_parent_is_invalidated_before_layout_runs_downward() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let trace = Rc::new(RefCell::new(Vec::new())); let child = rsc.ui.widgets.add_strong(TracedLeaf { height: 20.0, @@ -155,9 +151,7 @@ fn a_size_dependent_parent_is_invalidated_before_layout_runs_downward() { #[test] fn a_parent_that_ignores_child_size_is_not_invalidated_with_it() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let trace = Rc::new(RefCell::new(Vec::new())); let child = rsc.ui.widgets.add_strong(TracedLeaf { height: 20.0, @@ -183,9 +177,7 @@ fn a_parent_that_ignores_child_size_is_not_invalidated_with_it() { #[test] fn a_span_reuses_unchanged_sibling_sizes_when_only_its_along_extent_changes() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let changed_draws = Rc::new(Cell::new(0)); let sibling_draws = Rc::new(Cell::new(0)); let changed = rsc.ui.widgets.add_strong(CountedLeaf { @@ -230,9 +222,7 @@ fn a_span_reuses_unchanged_sibling_sizes_when_only_its_along_extent_changes() { #[test] fn a_child_coordinate_offset_moves_only_the_child_subtree() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let child = rsc.ui.widgets.add_strong(FixedRect(40.0)); let child_weak = child.weak(); let parent = rsc.ui.widgets.add_strong(ChildOffset { @@ -274,9 +264,7 @@ fn a_child_coordinate_offset_moves_only_the_child_subtree() { #[test] fn a_hinted_rest_draws_once_and_only_moves_the_fixed_child_after_it() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let first = rsc.ui.widgets.add_strong(FixedRect(40.0)); let fill = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); let fill = rsc.ui.widgets.add_strong(Sized { @@ -330,9 +318,7 @@ fn scrolled_rects( #[test] fn an_unchanged_frame_draws_and_rewrites_nothing() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (_scroll, root, _rects) = scrolled_rects(&mut rsc, 500); let mut render = UiRenderState::new(); render.resize((800.0, 20000.0)); @@ -348,9 +334,7 @@ fn an_unchanged_frame_draws_and_rewrites_nothing() { #[test] fn scrolling_moves_in_o1_without_a_redraw() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (scroll, root, _rects) = scrolled_rects(&mut rsc, 500); let mut render = UiRenderState::new(); render.resize((800.0, 600.0)); @@ -370,9 +354,7 @@ fn scrolling_moves_in_o1_without_a_redraw() { #[test] fn hit_testing_follows_a_scrolled_widget() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (scroll, root, rects) = scrolled_rects(&mut rsc, 500); let mut render = UiRenderState::new(); render.resize((800.0, 600.0)); @@ -398,9 +380,7 @@ fn hit_testing_follows_a_scrolled_widget() { #[test] fn redrawing_a_masked_widget_does_not_nest_its_own_mask() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (_scroll, inner_root, _rects) = scrolled_rects(&mut rsc, 8); let masked = rsc.ui.widgets.add_strong(Masked { shape: None, @@ -424,9 +404,7 @@ fn redrawing_a_masked_widget_does_not_nest_its_own_mask() { #[test] fn a_mask_stays_put_while_its_scrolled_content_moves() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (scroll, inner_root, _rects) = scrolled_rects(&mut rsc, 500); let masked = rsc.ui.widgets.add_strong(Masked { shape: None, @@ -473,9 +451,7 @@ fn composer_like_tree(rsc: &mut TestRsc) -> (WeakWidget, StrongWidget) #[test] fn composing_text_after_a_keyboard_resize_lands_in_the_bars_own_region() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (field, root) = composer_like_tree(&mut rsc); let mut render = UiRenderState::new(); @@ -515,9 +491,7 @@ fn composing_text_after_a_keyboard_resize_lands_in_the_bars_own_region() { #[test] fn a_scroll_measures_the_box_it_was_offered_not_the_window() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let rect = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); let tall = rsc.ui.widgets.add_strong(Sized { inner: rect.any(), @@ -565,9 +539,7 @@ fn a_scroll_measures_the_box_it_was_offered_not_the_window() { #[test] fn a_panned_widgets_own_hit_box_moves_exactly_once() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let rect = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); let tall = rsc.ui.widgets.add_strong(Sized { inner: rect.any(), @@ -601,9 +573,7 @@ fn a_panned_widgets_own_hit_box_moves_exactly_once() { #[test] fn a_masked_widget_keeps_one_mask_slot_that_is_always_its_own_region() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (_scroll, inner_root, _rects) = scrolled_rects(&mut rsc, 8); let masked = rsc.ui.widgets.add_strong(Masked { shape: None, @@ -648,9 +618,7 @@ fn a_masked_widget_keeps_one_mask_slot_that_is_always_its_own_region() { #[test] fn a_dp_cap_is_reported_in_pixels_so_a_span_can_place_it() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let rect = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); let tall = rsc.ui.widgets.add_strong(Sized { inner: rect.any(), @@ -690,9 +658,7 @@ fn a_dp_cap_is_reported_in_pixels_so_a_span_can_place_it() { #[test] fn a_size_independent_widget_moved_by_its_parent_has_the_hit_box_it_is_drawn_at() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let top = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); let spacer = rsc.ui.widgets.add_strong(Sized { inner: top.any(), @@ -757,9 +723,7 @@ impl Widget for MoveThenPlace { #[test] fn a_widget_moved_by_its_parent_and_then_placed_inside_it_lands_at_the_placement() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let rect = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); let child = rsc.ui.widgets.add_strong(Sized { inner: rect.any(), @@ -839,9 +803,7 @@ fn rounded_container(rsc: &mut TestRsc) -> (UiRenderState, MaskIdx, WidgetId, u3 #[test] fn a_masked_child_is_clipped_by_its_container_s_own_corner() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (render, mask, _child, slot) = rounded_container(&mut rsc); let corners = render.primitive_corners(slot, &rsc); let radius = render @@ -879,9 +841,7 @@ fn a_masked_child_is_clipped_by_its_container_s_own_corner() { #[test] fn a_mask_s_shape_decides_what_can_be_pressed() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (render, mask, _child, slot) = rounded_container(&mut rsc); let corners = render.primitive_corners(slot, &rsc); @@ -907,9 +867,7 @@ fn a_mask_s_shape_decides_what_can_be_pressed() { #[test] fn nested_masks_multiply_their_coverage() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let child = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); let child_id = child.id(); @@ -970,9 +928,7 @@ fn nested_masks_multiply_their_coverage() { #[test] fn a_plain_mask_still_clips_to_a_square_box() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (_scroll, inner_root, _rects) = scrolled_rects(&mut rsc, 8); let root = rsc .ui @@ -1002,9 +958,7 @@ fn a_plain_mask_still_clips_to_a_square_box() { #[test] fn a_scroll_area_opens_at_the_start_of_content_it_has_not_measured_yet() { for (name, pin, want) in [("read", Pin::Start, 0.0), ("written", Pin::End, 4900.0)] { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let fill = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)).any(); let tall = rsc.ui.widgets.add_strong(Sized { inner: fill, @@ -1037,9 +991,7 @@ fn a_span_of_padded_children_inside_a_span_draws_each_where_its_box_is() { const PAD: f32 = 4.0; const ROW: f32 = 20.0; const HEADER: f32 = 30.0; - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let header_fill = rsc.ui.widgets.add_strong(Rect::new(PaintId::RED)).any(); let header_id = header_fill.id(); let header = rsc.ui.widgets.add_strong(Sized { @@ -1120,9 +1072,7 @@ fn a_new_child_in_a_growing_lazy_row_uses_its_final_box_immediately() { const SECOND: f32 = 70.0; const GAP: f32 = 8.0; - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let first = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); let first_id = first.id(); let first = rsc.ui.widgets.add_strong(Sized { diff --git a/src/overlay.rs b/src/overlay.rs index bdfedc0..8bc5b77 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -402,10 +402,11 @@ pub trait OverlayRscExt: HasEvents { where W: WidgetLike, { + let render_handle = self.ui().render_state(); let (target, path) = self .events() .controllers - .path_to::(origin.id())?; + .path_to::(origin.id(), &render_handle.get())?; clear_singles(&path, self); let content = modal.add(self); @@ -447,10 +448,11 @@ pub trait OverlayRscExt: HasEvents { { match options.kind { OverlayKind::Single => { + let render_handle = self.ui().render_state(); let target = self .events() .controllers - .nearest_id::(origin.id())?; + .nearest_id::(origin.id(), &render_handle.get())?; let overlay = overlay.add_strong(self).any(); self.with_controller(target, |single: &mut SingleOverlayController, rsc| { single.open( @@ -463,10 +465,11 @@ pub trait OverlayRscExt: HasEvents { }) } OverlayKind::Stackable => { + let render_handle = self.ui().render_state(); let (target, path) = self .events() .controllers - .path_to::(origin.id())?; + .path_to::(origin.id(), &render_handle.get())?; clear_singles(&path, self); let overlay = overlay.add_strong(self).any(); open_stackable_at( diff --git a/src/sense_tests.rs b/src/sense_tests.rs index 0c8e3ed..f12164e 100644 --- a/src/sense_tests.rs +++ b/src/sense_tests.rs @@ -2,15 +2,15 @@ use crate::prelude::*; use std::{cell::Cell, rc::Rc, time::Instant}; struct SenseRsc { - ui: UiData, + ui: Ui, events: EventManager, } impl UiRsc for SenseRsc { - fn ui(&self) -> &UiData { + fn ui(&self) -> &Ui { &self.ui } - fn ui_mut(&mut self) -> &mut UiData { + fn ui_mut(&mut self) -> &mut Ui { &mut self.ui } fn on_draw(&mut self, active: &ActiveData) { @@ -28,6 +28,53 @@ impl HasState for SenseRsc { type State = (); } +#[derive(Clone)] +struct RenderProbe; + +impl Event for RenderProbe {} + +#[test] +fn an_event_can_read_the_completed_frame_while_mutating_ui_data() { + let mut rsc = SenseRsc { + ui: Ui::default(), + events: EventManager::default(), + }; + rsc.ui.resize((100.0, 100.0)); + let widget = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); + let weak = widget.weak(); + let saw_active = Rc::new(Cell::new(false)); + rsc.register_event(weak, RenderProbe, { + let saw_active = saw_active.clone(); + move |_, rsc| { + let render_handle = rsc.ui().render_state(); + let render_state = render_handle.get(); + saw_active.set(render_state.active.contains_key(&weak.id())); + rsc.widgets_mut().set_label(weak, "changed".to_string()); + assert!(render_state.active.contains_key(&weak.id())); + } + }); + rsc.draw(&widget.any()); + + rsc.run_event::(weak, (), &mut ()); + + assert!(saw_active.get()); + assert_eq!(rsc.widgets().label(weak), "changed"); +} + +#[test] +#[should_panic(expected = "render state cannot be mutated while it is being read")] +fn an_event_cannot_start_a_draw() { + let mut rsc = SenseRsc { + ui: Ui::default(), + events: EventManager::default(), + }; + let widget = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); + let weak = widget.weak(); + rsc.register_event(weak, RenderProbe, |_, rsc| rsc.draw(None)); + + rsc.run_event::(weak, (), &mut ()); +} + impl HasEvents for SenseRsc { fn events(&self) -> &EventManager { &self.events @@ -50,7 +97,7 @@ fn cursor_at(pos: Vec2) -> CursorState { #[test] fn a_button_over_a_list_scrolls_the_list_and_still_clicks() { let mut rsc = SenseRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), }; @@ -121,7 +168,7 @@ fn a_button_over_a_list_scrolls_the_list_and_still_clicks() { #[test] fn a_release_outside_every_hit_region_still_reaches_the_captured_widget() { let mut rsc = SenseRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), }; @@ -180,7 +227,7 @@ fn a_release_outside_every_hit_region_still_reaches_the_captured_widget() { #[test] fn capturing_one_widget_starves_every_other_widget_of_events() { let mut rsc = SenseRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), }; @@ -225,7 +272,7 @@ fn capturing_one_widget_starves_every_other_widget_of_events() { #[test] fn a_finger_drag_over_a_scroll_area_pans_it() { let mut rsc = SenseRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), }; @@ -316,7 +363,7 @@ fn the_clock_orders_samples_across_events() { #[test] fn a_scroll_area_that_captured_the_pointer_learns_its_gesture_ended() { let mut rsc = SenseRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), }; let scroll_strong = rect(PaintId::WHITE) @@ -370,7 +417,7 @@ fn a_scroll_area_that_captured_the_pointer_learns_its_gesture_ended() { #[test] fn taking_the_pointer_cancels_everyone_else_tracking_the_press() { let mut rsc = SenseRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), }; @@ -469,7 +516,7 @@ fn a_drag_pans_whichever_nested_scroll_area_owns_its_axis() { ), ] { let mut rsc = SenseRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), }; let seen = Rc::new(Cell::new(None)); @@ -526,7 +573,7 @@ fn a_drag_pans_whichever_nested_scroll_area_owns_its_axis() { #[test] fn a_press_does_not_reach_a_widget_the_pointer_has_just_left() { let mut rsc = SenseRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), }; diff --git a/src/widget/position/lazy_span.rs b/src/widget/position/lazy_span.rs index eabb8f4..bedaed8 100644 --- a/src/widget/position/lazy_span.rs +++ b/src/widget/position/lazy_span.rs @@ -833,14 +833,14 @@ mod tests { } struct TestRsc { - ui: UiData, + ui: Ui, } impl UiRsc for TestRsc { - fn ui(&self) -> &UiData { + fn ui(&self) -> &Ui { &self.ui } - fn ui_mut(&mut self) -> &mut UiData { + fn ui_mut(&mut self) -> &mut Ui { &mut self.ui } } @@ -882,9 +882,7 @@ mod tests { #[test] fn a_list_shorter_than_the_viewport_is_drawn_whole_and_stays_at_the_bottom() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); push_rows(&mut rsc, &mut list, &[0, 1, 2], 20.0); let (list_weak, root) = add_list(&mut rsc, list); @@ -912,9 +910,7 @@ mod tests { #[test] fn a_dir_up_span_grows_upward_from_item_zero() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::UP, Pin::End); let rows = push_rows(&mut rsc, &mut list, &[0, 1, 2], 20.0); let (list_weak, root) = add_list(&mut rsc, list); @@ -949,9 +945,7 @@ mod tests { #[test] fn a_delta_moves_both_directions_the_same_way_on_screen() { let moved_by = |dir: Dir| { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(dir, Pin::End); let keys: Vec = (0..10).collect(); let rows = push_rows(&mut rsc, &mut list, &keys, 20.0); @@ -1003,9 +997,7 @@ mod tests { #[test] fn a_reversed_span_hit_tests_in_screen_space() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::UP, Pin::End); push_rows(&mut rsc, &mut list, &[0, 1, 2], 20.0); let (list_weak, root) = add_list(&mut rsc, list); @@ -1030,9 +1022,7 @@ mod tests { #[test] fn bottom_anchored_by_default() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); push_rows(&mut rsc, &mut list, &[0, 1, 2, 3, 4], 20.0); let (list_weak, root) = add_list(&mut rsc, list); @@ -1078,9 +1068,7 @@ mod tests { fn a_row_that_changes_height_draws_its_background_at_the_new_height_immediately() { for key_to_change in 0..5u64 { for new_height in [50.0f32, 8.0] { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); let mut rows = Vec::new(); for key in 0..5u64 { @@ -1118,9 +1106,7 @@ mod tests { #[test] fn a_fill_shaped_background_is_not_left_oversized() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); let mut bg_ids = Vec::new(); for key in 0..5u64 { @@ -1148,9 +1134,7 @@ mod tests { #[test] fn insert_above_anchor_is_o1_and_does_not_move_visible_rows() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); push_rows(&mut rsc, &mut list, &[10, 11, 12], 20.0); let (list_weak, root) = add_list(&mut rsc, list); @@ -1188,9 +1172,7 @@ mod tests { #[test] fn expanding_a_row_holds_the_edge_nearest_the_tap() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); let rows = push_rows(&mut rsc, &mut list, &[0, 1, 2, 3, 4], 20.0); let (list_weak, root) = add_list(&mut rsc, list); @@ -1234,9 +1216,7 @@ mod tests { #[test] fn expanding_a_row_holds_the_bottom_edge_when_tap_is_lower() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); let rows = push_rows(&mut rsc, &mut list, &[0, 1, 2, 3, 4], 20.0); let (list_weak, root) = add_list(&mut rsc, list); @@ -1270,9 +1250,7 @@ mod tests { #[test] fn moves_stay_o1_across_list_size() { for &n in &[20usize, 200, 2000] { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); let keys: Vec = (0..n as u64).collect(); push_rows(&mut rsc, &mut list, &keys, 20.0); @@ -1300,9 +1278,7 @@ mod tests { #[test] fn a_large_accumulated_offset_rebases_without_moving_the_content() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); let keys: Vec = (0..100).collect(); let rows = push_rows(&mut rsc, &mut list, &keys, 1_000.0); @@ -1348,9 +1324,7 @@ mod tests { #[test] fn replacing_the_last_row_stays_pinned_to_the_bottom() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); push_rows(&mut rsc, &mut list, &[0, 1, 2, 3, 4], 20.0); let (list_weak, root) = add_list(&mut rsc, list); @@ -1392,9 +1366,7 @@ mod tests { #[test] fn replace_back_forgets_the_evicted_keys_own_height() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); push_rows(&mut rsc, &mut list, &[0, 1, 2, 3, 4], 20.0); let (list_weak, root) = add_list(&mut rsc, list); @@ -1429,9 +1401,7 @@ mod tests { #[test] fn replacing_the_last_row_out_of_view_does_not_move_visible_rows() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); push_rows(&mut rsc, &mut list, &[0, 1, 2, 3, 4], 20.0); let (list_weak, root) = add_list(&mut rsc, list); @@ -1474,9 +1444,7 @@ mod tests { #[test] fn replacing_the_last_row_many_times_does_not_leak_primitives() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); for key in 0..5u64 { let (_bg_id, row) = background_styled_row(&mut rsc, 20.0); @@ -1511,9 +1479,7 @@ mod tests { #[test] fn an_ancestor_redrawing_a_dirty_row_leaves_no_stale_copy() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let mut list = LazySpan::new(Dir::DOWN, Pin::End); let mut rows = Vec::new(); for key in 0..5u64 { @@ -1574,9 +1540,7 @@ mod tests { #[test] fn a_fling_moves_the_list_and_then_settles() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (scroll, root, mut render) = build_flingable_list(&mut rsc); let list_weak = scroll; @@ -1602,9 +1566,7 @@ mod tests { #[test] fn a_registered_fling_is_driven_by_tick_animations_and_then_unregisters() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (scroll, root, mut render) = build_flingable_list(&mut rsc); let list_weak = scroll; let before = scroll_position(rsc.ui.widgets.get(&list_weak).unwrap()); @@ -1637,9 +1599,7 @@ mod tests { #[test] fn a_negative_delta_moves_toward_the_end() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (scroll, root, mut render) = build_flingable_list(&mut rsc); let list_weak = scroll; rsc.ui.widgets.get_mut(&scroll).unwrap().scroll(2000.0); @@ -1661,9 +1621,7 @@ mod tests { #[test] fn amt_counts_only_what_the_child_could_take() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (scroll, root, mut render) = build_flingable_list(&mut rsc); rsc.ui.widgets.get_mut(&scroll).unwrap().scroll(100.0); render.update(&root, &mut rsc); @@ -1684,9 +1642,7 @@ mod tests { #[test] fn a_fling_stops_at_the_first_row() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (scroll, root, mut render) = build_flingable_list(&mut rsc); let list_weak = scroll; rsc.ui.widgets.get_mut(&scroll).unwrap().fling(50_000.0); @@ -1710,9 +1666,7 @@ mod tests { #[test] fn scrolling_past_the_start_lands_on_it_in_the_same_frame() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (scroll, root, mut render) = build_flingable_list(&mut rsc); let list_weak = scroll; rsc.ui.widgets.get_mut(&scroll).unwrap().scroll(100_000.0); @@ -1734,9 +1688,7 @@ mod tests { #[test] fn anchor_position_display_reports_slot_and_offset() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let (list_weak, root, mut render) = build_flingable_list(&mut rsc); let _ = (&root, &mut render); let list_ref = rsc.ui.widgets.get(&list_weak).unwrap(); diff --git a/src/widget/position/scroll_area.rs b/src/widget/position/scroll_area.rs index 87b0933..beafd14 100644 --- a/src/widget/position/scroll_area.rs +++ b/src/widget/position/scroll_area.rs @@ -87,7 +87,6 @@ mod tests { use super::*; use crate::layout_tests::TestRsc; use crate::sense::{CursorButton, DRAG_SLOP, PointerRequests}; - use iris_core::UiData; use std::time::Duration; fn area() -> (Fixture, WidgetId) { @@ -95,9 +94,7 @@ mod tests { } fn area_on(axis: Axis) -> (Fixture, WidgetId) { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let fill = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)).any(); let id = fill.id(); let long = Some(Len::abs(1000.0)); diff --git a/src/widget/text/edit.rs b/src/widget/text/edit.rs index b0e415d..1056524 100644 --- a/src/widget/text/edit.rs +++ b/src/widget/text/edit.rs @@ -498,7 +498,7 @@ pub trait TextEditable { impl> TextEditable for I { fn edit<'a>(&self, ui: &'a mut impl UiRsc) -> TextEditCtx<'a> { - let ui = ui.ui_mut(); + let ui: &mut UiData = ui.ui_mut(); TextEditCtx { text: ui.widgets.get_mut(self).unwrap(), data: &mut ui.text, diff --git a/src/widget/text/mod.rs b/src/widget/text/mod.rs index 8957a1f..42e50e6 100644 --- a/src/widget/text/mod.rs +++ b/src/widget/text/mod.rs @@ -222,9 +222,7 @@ mod tests { use crate::prelude::*; fn rendered_text(content: &str) -> (TestRsc, UiRenderState, WeakWidget, StrongWidget) { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let text = wtext(content).add_strong(&mut rsc); let id = text.weak(); let root = text.any(); @@ -306,9 +304,7 @@ mod tests { #[test] fn clearing_the_atlas_re_renders_cached_text_instead_of_reusing_it() { - let mut rsc = TestRsc { - ui: UiData::default(), - }; + let mut rsc = TestRsc { ui: Ui::default() }; let root = wtext("hello there") .size(18) .color(PaintId::WHITE) diff --git a/src/widget/text/selection.rs b/src/widget/text/selection.rs index 9e0b5d4..c6a6104 100644 --- a/src/widget/text/selection.rs +++ b/src/widget/text/selection.rs @@ -161,7 +161,7 @@ pub trait TextSelectable { impl> TextSelectable for I { fn selection<'a>(&self, ui: &'a mut impl UiRsc) -> TextSelectionCtx<'a> { - let ui = ui.ui_mut(); + let ui: &mut UiData = ui.ui_mut(); TextSelectionCtx { view: &mut ui.widgets.get_mut(self).unwrap().view, data: &mut ui.text, @@ -250,7 +250,7 @@ impl SelectionController { id: WidgetId, f: impl FnOnce(&mut TextSelectionCtx<'_>) -> T, ) -> Option { - let ui = rsc.ui_mut(); + let ui: &mut UiData = rsc.ui_mut(); let text = ui .widgets .get_dyn_mut(id)? @@ -481,16 +481,16 @@ mod controller_tests { use super::*; struct TestRsc { - ui: UiData, + ui: Ui, events: EventManager, } impl UiRsc for TestRsc { - fn ui(&self) -> &UiData { + fn ui(&self) -> &Ui { &self.ui } - fn ui_mut(&mut self) -> &mut UiData { + fn ui_mut(&mut self) -> &mut Ui { &mut self.ui } @@ -532,7 +532,7 @@ mod controller_tests { StrongWidget, ) { let mut rsc = TestRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), }; let first = wtext("first").add(&mut rsc); @@ -571,7 +571,7 @@ mod controller_tests { #[test] fn a_widget_without_an_order_override_keeps_draw_order() { let mut rsc = TestRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), }; let first = wtext("back").add(&mut rsc); @@ -604,7 +604,7 @@ mod controller_tests { #[test] fn nearest_controller_prefers_the_inner_scope() { let mut rsc = TestRsc { - ui: UiData::default(), + ui: Ui::default(), events: EventManager::default(), }; let leaf = wtext("leaf").add(&mut rsc); @@ -624,7 +624,7 @@ mod controller_tests { let found = rsc .events() .controllers - .nearest_id::(leaf.id()) + .nearest_id::(leaf.id(), &render) .unwrap(); assert_eq!(found.host(), inner.id()); } diff --git a/tests/color_space.rs b/tests/color_space.rs index 3758595..ff2b8a7 100644 --- a/tests/color_space.rs +++ b/tests/color_space.rs @@ -51,12 +51,7 @@ fn solid_paints_and_images_round_trip_through_an_srgb_target() { } fn render(gpu: &Gpu, renderer: &mut UiRenderNode, harness: &mut Harness) -> [[u8; 4]; 2] { - renderer.update( - &gpu.device, - &gpu.queue, - &mut harness.rsc.ui, - &mut harness.render, - ); + renderer.update(&gpu.device, &gpu.queue, &mut harness.rsc.ui); let texture = gpu.device.create_texture(&wgpu::TextureDescriptor { label: Some("Iris colour-space target"), size: wgpu::Extent3d {