use crate::core::{BaseState, CommandQueue};
use crate::piet::{BitmapTarget, Device, Piet};
use crate::*;
pub(crate) const DEFAULT_SIZE: Size = Size::new(400., 400.);
pub struct Harness<'a, T> {
piet: Piet<'a>,
inner: Inner<T>,
window_size: Size,
}
struct Inner<T> {
data: T,
env: Env,
window: Window<T>,
cmds: CommandQueue,
}
struct TargetGuard<'a>(Option<BitmapTarget<'a>>);
impl<T: Data> Harness<'_, T> {
pub fn create(data: T, root: impl Widget<T> + 'static, mut f: impl FnMut(&mut Harness<T>)) {
let mut device = Device::new().expect("harness failed to get device");
let target = device.bitmap_target(400, 400, 2.).expect("bitmap_target");
let mut target = TargetGuard(Some(target));
let piet = target.0.as_mut().unwrap().render_context();
let desc = WindowDesc::new(|| root);
let window = Window::new(WindowId::next(), Default::default(), desc);
let inner = Inner {
data,
env: theme::init(),
window,
cmds: Default::default(),
};
let mut harness = Harness {
piet,
inner,
window_size: DEFAULT_SIZE,
};
f(&mut harness);
}
pub fn set_initial_size(&mut self, size: Size) {
self.window_size = size;
}
pub fn window(&self) -> &Window<T> {
&self.inner.window
}
#[allow(dead_code)]
pub fn window_mut(&mut self) -> &mut Window<T> {
&mut self.inner.window
}
#[allow(dead_code)]
pub fn data(&self) -> &T {
&self.inner.data
}
pub(crate) fn get_state(&mut self, widget: WidgetId) -> BaseState {
match self.try_get_state(widget) {
Some(thing) => thing,
None => panic!("get_state failed for widget {:?}", widget),
}
}
pub(crate) fn try_get_state(&mut self, widget: WidgetId) -> Option<BaseState> {
let cell = StateCell::default();
let state_cell = cell.clone();
self.lifecycle(LifeCycle::DebugRequestState { widget, state_cell });
cell.take()
}
pub(crate) fn inspect_state(&mut self, f: impl Fn(&BaseState) + 'static) {
let checkfn = StateCheckFn::new(f);
self.lifecycle(LifeCycle::DebugInspectState(checkfn))
}
pub fn submit_command(&mut self, cmd: impl Into<Command>, target: impl Into<Option<Target>>) {
let target = target.into().unwrap_or_else(|| self.inner.window.id.into());
let event = Event::TargetedCommand(target, cmd.into());
self.event(event);
}
pub fn send_initial_events(&mut self) {
self.event(Event::WindowConnected);
self.event(Event::Size(self.window_size));
}
pub fn event(&mut self, event: Event) {
self.inner.event(event);
self.process_commands();
self.update();
}
fn process_commands(&mut self) {
loop {
let cmd = self.inner.cmds.pop_front();
match cmd {
Some((target, cmd)) => self.event(Event::TargetedCommand(target, cmd)),
None => break,
}
}
}
fn lifecycle(&mut self, event: LifeCycle) {
self.inner.lifecycle(event)
}
fn update(&mut self) {
self.inner.update()
}
pub fn just_layout(&mut self) {
self.inner.layout(&mut self.piet)
}
#[allow(dead_code)]
pub fn paint(&mut self) {
self.inner.paint(&mut self.piet)
}
}
impl<T: Data> Inner<T> {
fn event(&mut self, event: Event) {
self.window
.event(&mut self.cmds, event, &mut self.data, &self.env);
}
fn lifecycle(&mut self, event: LifeCycle) {
self.window
.lifecycle(&mut self.cmds, &event, &self.data, &self.env);
}
fn update(&mut self) {
self.window.update(&self.data, &self.env);
}
fn layout(&mut self, piet: &mut Piet) {
self.window.just_layout(piet, &self.data, &self.env);
}
#[allow(dead_code)]
fn paint(&mut self, piet: &mut Piet) {
self.window
.do_paint(piet, &mut self.cmds, &self.data, &self.env);
}
}
impl Drop for TargetGuard<'_> {
fn drop(&mut self) {
let _ = self
.0
.take()
.map(|t| t.into_raw_pixels(piet::ImageFormat::RgbaPremul));
}
}