use std::cell::Cell;
use std::rc::Rc;
use crate::animation::{clear_draw_request, signal_async_state_change, wants_draw};
use crate::{App, DrawCtx, Event, EventResult, Framebuffer, GfxCtx, Rect, Size, Widget};
struct DirtyCounter {
bounds: Rect,
marks: Rc<Cell<usize>>,
children: Vec<Box<dyn Widget>>,
}
impl Widget for DirtyCounter {
fn bounds(&self) -> Rect {
self.bounds
}
fn set_bounds(&mut self, b: Rect) {
self.bounds = b;
}
fn children(&self) -> &[Box<dyn Widget>] {
&self.children
}
fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
&mut self.children
}
fn layout(&mut self, available: Size) -> Size {
available
}
fn paint(&mut self, _ctx: &mut dyn DrawCtx) {}
fn on_event(&mut self, _event: &Event) -> EventResult {
EventResult::Ignored
}
fn mark_dirty(&mut self) {
self.marks.set(self.marks.get() + 1);
}
}
const VIEWPORT: Size = Size {
width: 64.0,
height: 64.0,
};
fn paint(app: &mut App) {
let mut fb = Framebuffer::new(VIEWPORT.width as u32, VIEWPORT.height as u32);
let mut ctx = GfxCtx::new(&mut fb);
app.paint(&mut ctx);
}
#[test]
fn signal_between_wants_draw_and_paint_reaches_the_paint() {
let marks = Rc::new(Cell::new(0));
let root = DirtyCounter {
bounds: Rect::default(),
marks: Rc::clone(&marks),
children: Vec::new(),
};
let mut app = App::new(Box::new(root));
app.layout(VIEWPORT);
clear_draw_request();
paint(&mut app);
marks.set(0);
let _ = wants_draw();
std::thread::spawn(signal_async_state_change)
.join()
.expect("signal thread");
paint(&mut app);
assert!(
marks.get() > 0,
"the paint must observe the cross-thread async signal (dirty walk), \
not discard it in clear_draw_request"
);
}