//! Rendering of application to display. use crate::*; pub(crate) use ratatui::prelude::CrosstermBackend; pub(crate) use ratatui::style::Style; pub(crate) use ratatui::layout::Rect; pub(crate) use ratatui::buffer::{Buffer, Cell}; /// Main thread render loop pub fn render_thread ( exited: &Arc, device: &Arc> ) -> Usually> { let exited = exited.clone(); let device = device.clone(); let mut terminal = ratatui::Terminal::new(CrosstermBackend::new(stdout()))?; let sleep = Duration::from_millis(20); Ok(spawn(move || loop { if let Ok(device) = device.try_read() { terminal.draw(|frame|{ let area = frame.size(); let buffer = frame.buffer_mut(); device .render(buffer, area) .expect("Failed to render content"); }) .expect("Failed to render frame"); } if exited.fetch_and(true, Ordering::Relaxed) { break } std::thread::sleep(sleep); })) } /// Trait for things that render to the display. pub trait Render: Send + Sync { // Render something to an area of the buffer. // Returns area used by component. // This is insufficient but for the most basic dynamic layout algorithms. fn render (&self, _b: &mut Buffer, _a: Rect) -> Usually { Ok(Rect { x: 0, y: 0, width: 0, height: 0 }) } fn into_collected <'a> (self) -> Collected<'a> where Self: Sized + 'a { Collected::Box(Box::new(self)) } } /// Implement the `Render` trait. #[macro_export] macro_rules! render { ($T:ty) => { impl Render for $T {} }; ($T:ty |$self:ident, $buf:ident, $area:ident|$block:expr) => { impl Render for $T { fn render (&$self, $buf: &mut Buffer, $area: Rect) -> Usually { $block } } }; ($T:ty = $render:path) => { impl Render for $T { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { $render(self, buf, area) } } } } impl Render for () { fn render (&self, _: &mut Buffer, a: Rect) -> Usually { Ok(Rect { x: a.x, y: a.y, width: 0, height: 0 }) } } impl Render for &T { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { (*self).render(buf, area) } fn into_collected <'a> (self) -> Collected<'a> where Self: Sized + 'a { Collected::Ref(self) } } impl Render for &mut T { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { (**self).render(buf, area) } fn into_collected <'a> (self) -> Collected<'a> where Self: Sized + 'a { Collected::Ref(self) } } impl Render for Option { fn render (&self, b: &mut Buffer, a: Rect) -> Usually { match self { Some(widget) => widget.render(b, a), None => ().render(b, a), } } } impl<'a> Render for Box { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { (**self).render(buf, area) } fn into_collected <'b> (self) -> Collected<'b> where Self: Sized + 'b { Collected::Box(self) } } //impl<'a, T: Fn(&mut Buffer, Rect) -> Usually + Send + Sync + 'a> Render for T { //fn render (&self, b: &mut Buffer, a: Rect) -> Usually { //(*self)(b, a) //} //} impl<'a> Render for Box Usually + Send + Sync + 'a> { fn render (&self, b: &mut Buffer, a: Rect) -> Usually { (*self)(b, a) } } impl Render for Arc> { fn render (&self, b: &mut Buffer, a: Rect) -> Usually { self.lock().unwrap().render(b, a) } } impl Render for Arc> { fn render (&self, b: &mut Buffer, a: Rect) -> Usually { self.read().unwrap().render(b, a) } } pub fn center_box (area: Rect, w: u16, h: u16) -> Rect { let width = w.min(area.width * 3 / 5); let height = h.min(area.width * 3 / 5); let x = area.x + (area.width - width) / 2; let y = area.y + (area.height - height) / 2; Rect { x, y, width, height } } pub fn half_block (lower: bool, upper: bool) -> Option { match (lower, upper) { (true, true) => Some('█'), (true, false) => Some('▄'), (false, true) => Some('▀'), _ => None } } pub trait Blit { // Render something to X, Y coordinates in a buffer, ignoring width/height. fn blit (&self, buf: &mut Buffer, x: u16, y: u16, style: Option