use yog_api::ui::event::keys;
use yog_api::ui::widget::WidgetKind;
use yog_api::ui::{LayoutNode, Rect, StateFlags, UiRoot, Widget};
use crate::host::{find_path, Host};
use crate::{ScreenHandle, View};
pub struct TestHost<W: View> {
host: Host<W>,
}
impl<W: View> TestHost<W> {
pub fn new(vm: W::Vm) -> Self {
let mut host = Host::new(vm, ScreenHandle::new(W::META), false);
host.size = (427.0, 240.0);
Self { host }
}
pub fn size(&mut self, w: f32, h: f32) -> &mut Self {
self.host.size = (w, h);
self
}
pub fn open(&mut self) -> &mut Self {
let (w, h) = self.host.size;
self.event(&format!("open:{w}:{h}"));
self
}
pub fn close(&mut self) -> &mut Self {
self.event("close");
self
}
pub fn frame(&mut self) -> &mut Self {
let (w, h) = self.host.size;
self.host.frame(w, h);
self
}
pub fn event(&mut self, raw: &str) -> bool {
let used = self.host.on_event(raw);
self.frame();
used
}
pub fn click_path(&mut self, path: &[u16]) -> bool {
let Some(r) = self.host.content_node(path).map(|n| n.rect) else {
panic!("yog-kit test: nothing laid out at {path:?}");
};
let (x, y) = (r.x + r.w / 2.0, r.y + r.h / 2.0);
self.move_to(x, y);
let used = self.event(&format!("click:{x}:{y}"));
self.event(&format!("release:{x}:{y}"));
used
}
pub fn click_id(&mut self, id: &str) -> bool {
let path = self.path(id);
self.click_path(&path)
}
pub fn move_to(&mut self, x: f32, y: f32) -> &mut Self {
self.event(&format!("move:{x}:{y}"));
self
}
pub fn drag(&mut self, points: &[(f32, f32)]) -> bool {
let Some((&(x0, y0), rest)) = points.split_first() else {
return false;
};
self.move_to(x0, y0);
let used = self.event(&format!("click:{x0}:{y0}"));
for &(x, y) in rest {
self.event(&format!("drag:{x}:{y}"));
}
let (x, y) = points[points.len() - 1];
self.event(&format!("release:{x}:{y}"));
used
}
pub fn scroll(&mut self, dy: f32) -> bool {
self.event(&format!("scroll:{dy}"))
}
pub fn key(&mut self, key: i32) -> bool {
self.key_mods(key, 0)
}
pub fn key_mods(&mut self, key: i32, mods: i32) -> bool {
self.event(&format!("key:{key}:{mods}"))
}
pub fn type_text(&mut self, text: &str) -> &mut Self {
for c in text.chars() {
self.event(&format!("char:{}", c as u32));
}
self
}
pub fn type_into(&mut self, id: &str, text: &str) -> &mut Self {
self.click_id(id);
self.key(keys::END);
self.type_text(text)
}
pub fn vm(&self) -> &W::Vm {
&self.host.vm
}
pub fn vm_mut(&mut self) -> &mut W::Vm {
self.host.dirty = true;
&mut self.host.vm
}
pub fn handle(&self) -> ScreenHandle<W::Vm> {
self.host.handle.clone()
}
pub fn root(&self) -> &Widget {
self.host.content()
}
pub fn ui(&self) -> &UiRoot {
&self.host.ui
}
pub fn path(&self, id: &str) -> Vec<u16> {
find_path(self.root(), id).unwrap_or_else(|| panic!("yog-kit test: no widget with id {id:?}"))
}
pub fn widget(&self, id: &str) -> Option<&Widget> {
find_path(self.root(), id).and_then(|p| self.root().get_path(&p))
}
pub fn text_at(&self, path: &[u16]) -> Option<String> {
match &self.root().get_path(path)?.kind {
WidgetKind::Label(t) | WidgetKind::Button(t) => Some(t.clone()),
WidgetKind::TextBox(tb) => Some(tb.text.clone()),
_ => None,
}
}
pub fn text(&self, id: &str) -> Option<String> {
self.text_at(&find_path(self.root(), id)?)
}
pub fn is_visible(&self, id: &str) -> bool {
let Some(p) = find_path(self.root(), id) else {
return false;
};
(0..=p.len()).all(|n| self.root().get_path(&p[..n]).is_some_and(|w| w.visible))
}
pub fn state(&self, id: &str) -> StateFlags {
self.widget(id).map_or(StateFlags::NONE, |w| w.state)
}
pub fn node(&self, id: &str) -> Option<&LayoutNode> {
self.host.content_node(&find_path(self.root(), id)?)
}
pub fn rect(&self, id: &str) -> Option<Rect> {
self.node(id).map(|n| n.rect)
}
pub fn scroll_offset(&self, i: usize) -> f32 {
self.host.parts.scrollers[i].state.offset
}
pub fn requests(&self) -> &[String] {
&self.host.log
}
pub fn closed(&self) -> bool {
self.host.log.iter().any(|r| r == "close")
}
}