mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-09 05:06:43 +01:00
wip: generic render
This commit is contained in:
parent
7bd2a70e85
commit
fcd2d16de9
12 changed files with 314 additions and 351 deletions
|
|
@ -2,230 +2,60 @@
|
|||
|
||||
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<AtomicBool>,
|
||||
device: &Arc<RwLock<impl Render + 'static>>
|
||||
) -> Usually<JoinHandle<()>> {
|
||||
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 are displayed to the user.
|
||||
pub trait Render<T, U>: Send + Sync {
|
||||
fn render (&self, to: &mut T) -> Perhaps<U>;
|
||||
}
|
||||
|
||||
/// 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<Rect> {
|
||||
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<Rect> {
|
||||
$block
|
||||
}
|
||||
}
|
||||
};
|
||||
($T:ty = $render:path) => {
|
||||
impl Render for $T {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
$render(self, buf, area)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for () {
|
||||
fn render (&self, _: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
Ok(Rect { x: a.x, y: a.y, width: 0, height: 0 })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Render> Render for &T {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
(*self).render(buf, area)
|
||||
}
|
||||
fn into_collected <'a> (self) -> Collected<'a> where Self: Sized + 'a {
|
||||
Collected::Ref(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Render> Render for &mut T {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
(**self).render(buf, area)
|
||||
}
|
||||
fn into_collected <'a> (self) -> Collected<'a> where Self: Sized + 'a {
|
||||
Collected::Ref(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Render> Render for Option<T> {
|
||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
/// Options can be rendered optionally.
|
||||
impl<R, T, U> Render<T, U> for Option<R> where R: Render<T, U> {
|
||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
||||
match self {
|
||||
Some(widget) => widget.render(b, a),
|
||||
None => ().render(b, a),
|
||||
Some(component) => component.render(to),
|
||||
None => Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Render for Box<dyn Render + 'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
(**self).render(buf, area)
|
||||
}
|
||||
fn into_collected <'b> (self) -> Collected<'b> where Self: Sized + 'b {
|
||||
Collected::Box(self)
|
||||
/// Boxed references can be rendered.
|
||||
impl<'a, T, U> Render<T, U> for Box<dyn Render<T, U> + 'a> {
|
||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
||||
(**self).render(to)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Render> Render for Arc<T> {
|
||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
self.as_ref().render(b, a)
|
||||
/// Immutable references can be rendered.
|
||||
impl<R, T, U> Render<T, U> for &R where R: Render<T, U> {
|
||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
||||
(*self).render(to)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Render> Render for Mutex<T> {
|
||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
self.lock().unwrap().render(b, a)
|
||||
/// Mutable references can be rendered.
|
||||
impl<R, T, U> Render<T, U> for &mut R where R: Render<T, U> {
|
||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
||||
(**self).render(to)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Render + Sync> Render for RwLock<T> {
|
||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
self.read().unwrap().render(b, a)
|
||||
/// Counted references can be rendered.
|
||||
impl<R, T, U> Render<T, U> for Arc<R> where R: Render<T, U> {
|
||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
||||
self.as_ref().render(to)
|
||||
}
|
||||
}
|
||||
|
||||
//impl<'a, T: Fn(&mut Buffer, Rect) -> Usually<Rect> + Send + Sync + 'a> Render for T {
|
||||
//fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
//(*self)(b, a)
|
||||
//}
|
||||
//}
|
||||
|
||||
impl<'a> Render for Box<dyn Fn(&mut Buffer, Rect) -> Usually<Rect> + Send + Sync + 'a> {
|
||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
(*self)(b, a)
|
||||
/// References behind a [Mutex] can be rendered.
|
||||
impl<R, T, U> Render<T, U> for Mutex<R> where R: Render<T, U> {
|
||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
||||
self.lock().unwrap().render(to)
|
||||
}
|
||||
}
|
||||
|
||||
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<char> {
|
||||
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<Style>) -> Usually<Rect>;
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> Blit for T {
|
||||
fn blit (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>) -> Usually<Rect> {
|
||||
if x < buf.area.width && y < buf.area.height {
|
||||
buf.set_string(x, y, self.as_ref(), style.unwrap_or(Style::default()));
|
||||
}
|
||||
Ok(Rect { x, y, width: self.as_ref().len() as u16, height: 1 })
|
||||
}
|
||||
}
|
||||
|
||||
//impl WidgetRef for &dyn Render {
|
||||
//fn render_ref (&self, area: Rect, buf: &mut Buffer) {
|
||||
//Render::render(*self, buf, area).expect("Failed to render device.");
|
||||
//}
|
||||
//}
|
||||
|
||||
//impl WidgetRef for dyn Render {
|
||||
//fn render_ref (&self, area: Rect, buf: &mut Buffer) {
|
||||
//Render::render(self, buf, area).expect("Failed to render device.");
|
||||
//}
|
||||
//}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BigBuffer {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub content: Vec<Cell>
|
||||
}
|
||||
|
||||
impl BigBuffer {
|
||||
pub fn new (width: usize, height: usize) -> Self {
|
||||
Self { width, height, content: vec![Cell::default(); width*height] }
|
||||
}
|
||||
pub fn get (&self, x: usize, y: usize) -> Option<&Cell> {
|
||||
let i = self.index_of(x, y);
|
||||
self.content.get(i)
|
||||
}
|
||||
pub fn get_mut (&mut self, x: usize, y: usize) -> Option<&mut Cell> {
|
||||
let i = self.index_of(x, y);
|
||||
self.content.get_mut(i)
|
||||
}
|
||||
pub fn index_of (&self, x: usize, y: usize) -> usize {
|
||||
y * self.width + x
|
||||
}
|
||||
}
|
||||
|
||||
pub struct If<'a>(pub bool, pub &'a (dyn Render + Sync));
|
||||
|
||||
impl<'a> Render for If<'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
match self.0 {
|
||||
true => self.1 as &dyn Render,
|
||||
false => &() as &dyn Render
|
||||
}.render(buf, area)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IfElse<'a>(pub bool, pub &'a (dyn Render + Sync), pub &'a (dyn Render + Sync));
|
||||
|
||||
impl<'a> Render for IfElse<'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
match self.0 {
|
||||
true => self.1 as &dyn Render,
|
||||
false => &() as &dyn Render
|
||||
}.render(buf, area)
|
||||
/// References behind a [RwLock] can be rendered.
|
||||
impl<R, T, U> Render<T, U> for RwLock<R> where R: Render<T, U> {
|
||||
fn render (&self, to: &mut T) -> Perhaps<U> {
|
||||
self.read().unwrap().render(to)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue