//! A ui with no window: build a tree, run frames, move a pointer, and read //! back where widgets landed. //! //! It does not draw. A claim about pixels still needs a real surface. use crate::prelude::*; use std::{ sync::{ Arc, mpsc::{Receiver, SyncSender, sync_channel}, }, time::Duration, }; /// There is no loop here to post to, so updates queue until the test asks /// for them. struct Queue(SyncSender>>>); impl TaskQueue> for Queue { fn send(&self, update: Box>>) { let _ = self.0.send(update); } } #[derive(Default)] pub struct HarnessState { pub root: Option, } impl HasRoot for HarnessState { fn set_root(&mut self, root: StrongWidget) { self.root = Some(root); } } pub struct Harness { pub rsc: DefaultRsc, pub render: UiRenderState, pub state: HarnessState, updates: Receiver>>>, cursor: CursorState, } impl Harness { /// `size` is the output in physical pixels. pub fn new(size: impl Into) -> Self { // A `TaskQueue` must be `Sync`, which `mpsc::Sender` is not; the // bound that comes with `SyncSender` is far past anything a test // leaves unread. let (send, updates) = sync_channel(1024); let rsc = DefaultRsc::init(Arc::new(Queue(send))); let mut render = UiRenderState::new(); render.resize(size); Self { rsc, render, state: HarnessState::default(), updates, cursor: CursorState::default(), } } pub fn size(&self) -> Vec2 { self.render.output_size() } pub fn resize(&mut self, size: impl Into) { self.render.resize(size); } /// Sets the root and lays it out, so a pointer event has something to hit. pub fn set_root(&mut self, widget: impl WidgetLike, T>) { widget.set_root(&mut self.rsc, &mut self.state); self.frame(); } pub fn needs_redraw(&self) -> bool { self.render .needs_redraw(&self.state.root, self.rsc.widgets()) } pub fn apply_updates(&mut self) -> usize { let mut applied = 0; while let Ok(update) = self.updates.try_recv() { update(&mut self.state, &mut self.rsc); applied += 1; } applied } /// Waits for a task's first update, then applies everything waiting. /// False if none arrived in time. #[must_use] pub fn await_update(&mut self, timeout: Duration) -> bool { let Ok(update) = self.updates.recv_timeout(timeout) else { return false; }; update(&mut self.state, &mut self.rsc); self.apply_updates(); true } /// Lays the tree out and builds its primitives. pub fn frame(&mut self) { self.apply_updates(); self.render.update(&self.state.root, &mut self.rsc); } /// Where the last frame put a widget, or `None` if it drew nothing. pub fn region(&self, id: &impl IdLike) -> Option { self.render.window_region(id) } pub fn move_to(&mut self, pos: impl Into) { self.cursor.pos = pos.into(); self.cursor.exists = true; self.sense(); } pub fn leave(&mut self) { self.cursor.exists = false; self.sense(); } pub fn press(&mut self, button: CursorButton) { self.button(button).update(true); self.sense(); } pub fn release(&mut self, button: CursorButton) { self.button(button).update(false); self.sense(); } /// A wheel carries no position, so this goes wherever /// [`move_to`](Self::move_to) last put the cursor -- nowhere, until it has /// been called. pub fn scroll(&mut self, delta: impl Into) { self.cursor.scroll_delta = delta.into(); self.sense(); } pub fn click(&mut self, pos: impl Into) { self.move_to(pos); self.press(CursorButton::Left); self.release(CursorButton::Left); } fn button(&mut self, button: CursorButton) -> &mut ActivationState { let buttons = &mut self.cursor.buttons; match button { CursorButton::Left => &mut buttons.left, CursorButton::Middle => &mut buttons.middle, CursorButton::Right => &mut buttons.right, } } /// Dispatches against the last [`frame`](Self::frame)'s layout, which is /// what a window delivers input against too. fn sense(&mut self) { let cursor = self.cursor.clone(); let size = self.render.output_size(); self.render .run_sensors(&mut self.rsc, &mut self.state, cursor, size); self.cursor.end_frame(); } }