use std::borrow::{Borrow, BorrowMut};
use crate::{
AccessAction, AccessRequest, App, Core, Extension, Extensions, InputEvent, Key, KeyCode,
KeyMods, KeyPress, MouseButton, Rect, Size, UiEvent, Vec2,
};
pub struct Drive<C: BorrowMut<Core> = Core> {
pub core: C,
pub exts: Extensions,
pub viewport: Size,
pub scale: f32,
framing: bool,
frame: u64,
now: f64,
log: Vec<String>,
}
impl<C: BorrowMut<Core>> Drive<C> {
pub fn new(mut core: C, w: f32, h: f32) -> Self {
core.borrow_mut().set_inspect(true);
Drive {
core,
exts: Extensions::new(),
viewport: Size::new(w, h),
scale: 1.0,
framing: false,
frame: 0,
now: 0.0,
log: Vec::new(),
}
}
pub fn framing(mut self) -> Self {
self.framing = true;
self
}
fn core(&mut self) -> &mut Core {
self.core.borrow_mut()
}
pub fn extension(&mut self, ns: &str, ext: impl Extension + 'static) -> Result<(), String> {
self.exts.push_as(ns, Box::new(ext))
}
pub fn frame(&mut self, app: &mut impl App) {
self.frame += 1;
let (viewport, scale, now) = (self.viewport, self.scale, self.now);
let core = self.core.borrow_mut();
core.set_time(now);
let mut ui = core.frame_with(viewport, scale, &mut self.exts);
app.view(&mut ui);
ui.finish();
let pending = self.core().take_pending_events();
self.deliver(app, pending);
}
pub fn advance(&mut self, secs: f64) {
self.now += secs;
}
pub fn frames(&self) -> u64 {
self.frame
}
pub fn key_of(&mut self, label: &str) -> Option<Key> {
self.core().key_of(label)
}
pub fn rect_of(&self, key: Key) -> Option<Rect> {
let core: &Core = self.core.borrow();
let hit = core
.interaction
.hits()
.iter()
.rev()
.find(|h| h.key == key)
.map(|h| h.rect);
hit.or_else(|| core.nodes().iter().find(|n| n.key == key).map(|n| n.rect))
}
pub fn texts_under(&self, label: &str) -> Vec<String> {
let nodes = Borrow::<Core>::borrow(&self.core).nodes();
let Some(at) = nodes.iter().position(|n| n.label.as_deref() == Some(label)) else {
return Vec::new();
};
nodes[at + 1..]
.iter()
.take_while(|n| n.depth > nodes[at].depth)
.filter_map(|n| n.text.clone())
.collect()
}
pub fn warnings(&mut self) -> Vec<String> {
self.core()
.take_warnings()
.into_iter()
.map(|w| format!("{}: {}", w.code, w.message))
.collect()
}
pub fn input(&mut self, app: &mut impl App, ev: InputEvent) -> Vec<UiEvent> {
let out = self.core().handle_input(ev);
self.deliver(app, out.clone());
out
}
fn done(&mut self, app: &mut impl App, out: Vec<UiEvent>) -> Vec<UiEvent> {
if self.framing {
self.frame(app);
}
out
}
pub fn move_to(&mut self, app: &mut impl App, x: f32, y: f32) -> Vec<UiEvent> {
let out = self.input(app, InputEvent::CursorMoved(Vec2::new(x, y)));
self.done(app, out)
}
pub fn hover(&mut self, app: &mut impl App, key: Key) -> Vec<UiEvent> {
let Some(c) = self.rect_of(key).map(|r| r.center()) else {
return Vec::new();
};
self.move_to(app, c.x, c.y)
}
fn press_at(&mut self, app: &mut impl App, at: Vec2, clicks: u8) -> Vec<UiEvent> {
let mut out = self.input(app, InputEvent::CursorMoved(at));
out.extend(self.input(app, InputEvent::mouse_down(clicks)));
out.extend(self.input(app, InputEvent::mouse_up()));
out
}
pub fn click(&mut self, app: &mut impl App, x: f32, y: f32) -> Vec<UiEvent> {
let out = self.press_at(app, Vec2::new(x, y), 1);
self.done(app, out)
}
pub fn double_click(&mut self, app: &mut impl App, x: f32, y: f32) -> Vec<UiEvent> {
let at = Vec2::new(x, y);
let mut out = self.press_at(app, at, 1);
out.extend(self.press_at(app, at, 2));
self.done(app, out)
}
pub fn click_key(&mut self, app: &mut impl App, key: Key) -> Vec<UiEvent> {
let out = self.input(
app,
InputEvent::Access(AccessRequest::new(key, AccessAction::Click)),
);
self.done(app, out)
}
pub fn drag(&mut self, app: &mut impl App, from: Vec2, to: Vec2) -> Vec<UiEvent> {
let mut out = self.input(app, InputEvent::CursorMoved(from));
out.extend(self.input(app, InputEvent::mouse_down(1)));
let past = Vec2::new(from.x + 8.0, from.y + 8.0);
out.extend(self.input(app, InputEvent::CursorMoved(past)));
out.extend(self.input(app, InputEvent::CursorMoved(to)));
if self.framing {
self.frame(app);
}
out.extend(self.input(app, InputEvent::mouse_up()));
self.done(app, out)
}
pub fn wheel(&mut self, app: &mut impl App, x: f32, y: f32, dx: f32, dy: f32) -> Vec<UiEvent> {
let mut out = self.input(app, InputEvent::CursorMoved(Vec2::new(x, y)));
out.extend(self.input(app, InputEvent::Scroll(Vec2::new(dx, dy))));
self.done(app, out)
}
pub fn scroll_gesture(
&mut self,
app: &mut impl App,
x: f32,
y: f32,
delta: Vec2,
begins: bool,
) -> Vec<UiEvent> {
let mut out = self.input(app, InputEvent::CursorMoved(Vec2::new(x, y)));
out.extend(self.input(app, InputEvent::ScrollGesture { delta, begins }));
self.done(app, out)
}
pub fn button_click(
&mut self,
app: &mut impl App,
x: f32,
y: f32,
button: MouseButton,
) -> Vec<UiEvent> {
let mut out = self.input(app, InputEvent::CursorMoved(Vec2::new(x, y)));
out.extend(self.input(app, InputEvent::MouseDown { button, clicks: 1 }));
out.extend(self.input(app, InputEvent::MouseUp { button }));
self.done(app, out)
}
pub fn key(&mut self, app: &mut impl App, name: &str, mods: KeyMods) -> Vec<UiEvent> {
let code = KeyCode::from_name(name).unwrap_or(KeyCode::Unknown);
let mut press = KeyPress::new(code, mods);
let chord = mods.ctrl || mods.alt || mods.super_key;
match code {
KeyCode::Char(c) if !chord => press = press.with_text(c.to_string()),
KeyCode::Space if !chord => press = press.with_text(" "),
_ => {}
}
let mut out = self.input(app, InputEvent::KeyDown(press.clone()));
if let Some(ev) = press.edit_event() {
out.extend(self.input(app, ev));
}
out.extend(self.input(app, InputEvent::KeyUp(press.released())));
self.done(app, out)
}
pub fn keys(&mut self, app: &mut impl App, seq: &str) -> Vec<UiEvent> {
let mut out = Vec::new();
for c in seq.chars() {
let name = if c == ' ' {
"space".to_string()
} else {
c.to_string()
};
out.extend(self.key(app, &name, KeyMods::NONE));
}
out
}
pub fn text(&mut self, app: &mut impl App, s: &str) -> Vec<UiEvent> {
let out = self.input(app, InputEvent::Text(s.to_string()));
self.done(app, out)
}
pub fn commit(&mut self, app: &mut impl App, s: &str) -> Vec<UiEvent> {
let out = self.input(app, InputEvent::Commit(s.to_string()));
self.done(app, out)
}
pub fn focus(&mut self, app: &mut impl App, key: Key) -> Vec<UiEvent> {
let out = self.input(
app,
InputEvent::Access(AccessRequest::new(key, AccessAction::Focus)),
);
self.done(app, out)
}
pub fn check(&self, cond: bool, what: &str) -> Result<(), String> {
if cond {
println!(" ok {what}");
Ok(())
} else {
Err(what.to_string())
}
}
pub fn log(&self) -> &[String] {
&self.log
}
fn deliver(&mut self, app: &mut impl App, events: Vec<UiEvent>) {
for ev in &events {
self.log.push(format!(
"{:>4} {:08x} {}",
self.frame,
ev.key.0 as u32,
crate::devtools::fmt_value(&ev.payload)
));
}
let core = self.core.borrow_mut();
self.exts.route(events, |ev| app.on_event_with(ev, core));
}
}