use std::{cell::RefCell, io, rc::Rc};
use crossterm::event::{Event, KeyModifiers, MouseEvent, MouseEventKind};
use crate::{
AnyElement, ComponentDrawer, ElementRepr, render::tree::Tree, terminal::UpdaterTerminal,
};
use ratatui::{backend::TestBackend, buffer::Buffer};
pub(crate) type TestLog = Rc<RefCell<Vec<String>>>;
pub(crate) fn log_of(entries: &[&str]) -> Vec<String> {
entries.iter().map(|s| (*s).to_string()).collect()
}
struct NoopUpdater;
impl UpdaterTerminal for NoopUpdater {
fn insert_before(
&mut self,
_height: u16,
_draw_fn: Box<dyn FnOnce(&mut Buffer)>,
) -> io::Result<()> {
Ok(())
}
}
pub(crate) struct Mounted {
tree: Tree<'static>,
terminal: ratatui::Terminal<TestBackend>,
noop: NoopUpdater,
}
impl Mounted {
pub(crate) fn mount(el: impl Into<AnyElement<'static>>, mouse: bool) -> Self {
Self::mount_sized(el, mouse, 40, 10)
}
pub(crate) fn mount_sized(
el: impl Into<AnyElement<'static>>,
mouse: bool,
width: u16,
height: u16,
) -> Self {
let el: &'static mut AnyElement<'static> = Box::leak(Box::new(el.into()));
let helper = el.helper();
let props = el.props_mut();
let mut this = Self {
tree: Tree::new_with_mouse(props, helper, mouse),
terminal: ratatui::Terminal::new(TestBackend::new(width, height)).unwrap(),
noop: NoopUpdater,
};
this.frame();
this
}
fn frame(&mut self) {
for _ in 0..16 {
self.tree.update_once(&mut self.noop);
let mut redraw = false;
self.terminal
.draw(|frame| {
let area = frame.area();
let mut drawer = ComponentDrawer::new(frame, area);
redraw = self.tree.draw_root(&mut drawer);
})
.unwrap();
if !redraw {
return;
}
}
panic!("pointer layout synchronization did not settle within 16 frames");
}
pub(crate) fn dispatch(&mut self, kind: MouseEventKind, col: u16, row: u16) -> bool {
let redraw = self
.tree
.system_context_mut()
.input
.dispatch(Event::Mouse(MouseEvent {
kind,
column: col,
row,
modifiers: KeyModifiers::NONE,
}));
self.frame();
redraw
}
pub(crate) fn resize(&mut self, width: u16, height: u16) {
self.terminal.backend_mut().resize(width, height);
self.terminal
.resize(ratatui::layout::Rect::new(0, 0, width, height))
.unwrap();
self.tree
.system_context_mut()
.input
.dispatch(Event::Resize(width, height));
self.frame();
}
pub(crate) fn buffer(&mut self) -> Buffer {
self.terminal.backend().buffer().clone()
}
}
impl Mounted {
pub(crate) fn dispatch_key(&mut self, code: crossterm::event::KeyCode) {
self.tree
.system_context_mut()
.input
.dispatch(Event::Key(code.into()));
self.frame();
}
}
pub(crate) fn find_text(buf: &Buffer, needle: &str) -> Option<(u16, u16)> {
let area = buf.area();
for row in area.top()..area.bottom() {
for column in area.left()..area.right() {
let suffix: String = (column..area.right())
.map(|x| buf[(x, row)].symbol())
.collect();
if suffix.starts_with(needle) {
return Some((column, row));
}
}
}
None
}