tek/src/core/render.rs
2024-07-10 21:53:38 +03:00

195 lines
5.7 KiB
Rust

use crate::core::*;
pub(crate) use ratatui::prelude::*;
pub(crate) use ratatui::buffer::Cell;
use ratatui::widgets::WidgetRef;
pub fn fill_bg (buf: &mut Buffer, area: Rect, color: Color) {
let Rect { x, y, width, height } = area;
for y in y..y+height {
if y >= buf.area.height {
break
}
for x in x..x+width {
if x >= buf.area.width {
break
}
buf.get_mut(x, y).set_bg(color);
}
}
}
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 })
}
}
/// Trait for things that render to the display.
pub trait Render: Send {
// 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 })
}
}
/// Implement the `Render` trait.
#[macro_export] macro_rules! render {
($T:ty) => {
impl Render for $T {}
};
($T:ty |$self:ident, $buf:ident, $area:ident|$block:tt) => {
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: Fn(&mut Buffer, Rect) -> Usually<Rect> + Send> Render for T {
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
(*self)(b, a)
}
}
impl Render for Box<dyn Device> {
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
(**self).render(b, a)
}
}
impl<T: Render> Render for Arc<Mutex<T>> {
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
self.lock().unwrap().render(b, a)
}
}
impl<T: Render + Sync> Render for Arc<RwLock<T>> {
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
self.read().unwrap().render(b, a)
}
}
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.");
}
}
pub struct LineBuffer {
width: usize,
cells: Vec<Cell>,
style: Option<Style>,
bg: Cell,
}
impl LineBuffer {
pub fn new (bg: Cell, width: usize, height: usize) -> Self {
Self {
style: None,
width: width,
cells: vec![bg.clone();width*height],
bg,
}
}
pub fn height (&self) -> usize {
self.cells.len() / self.width
}
pub fn style (&mut self, style: Style) -> &mut Self {
self.style = Some(style);
self
}
pub fn no_style (&mut self) -> &mut Self {
self.style = None;
self
}
pub fn put (&mut self, data: &str, x: usize, y: usize) -> &mut Self {
if x < self.width {
for (i, c) in data.chars().enumerate() {
if x + i >= self.width {
break;
}
let index = y * self.width + x + i;
while index >= self.cells.len() {
self.cells.extend_from_slice(&vec![self.bg.clone();self.width]);
}
self.cells[index].set_char(c);
if let Some(s) = self.style {
self.cells[index].set_style(s);
}
}
}
self
}
pub fn show (&self, buf: &mut Buffer, area: Rect, offset: isize) -> Usually<Rect> {
let Rect { x, mut y, width, height } = area;
for row in offset..self.height() as isize {
let length = self.cells.len();
let start = ((row.max(0) as usize)*self.width).min(length);
let end = (((row + 1).max(0) as usize)*self.width).min(length);
for (column, cell) in self.cells[start..end].iter().enumerate() {
if column >= width as usize {
break
}
*buf.get_mut(x + column as u16, y + row as u16) = cell.clone();
}
y = y + 1;
if y > height {
break
}
}
Ok(area)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_line_buffer () {
let mut buffer = LineBuffer::new(Cell::default(), 12, 0);
assert_eq!(buffer.cells.len(), 0);
buffer.put("FOO", 0, 0);
assert_eq!(buffer.cells.len(), 12);
buffer.put("FOO", 6, 0);
assert_eq!(buffer.cells.len(), 12);
buffer.put("FOO", 11, 0);
assert_eq!(buffer.cells.len(), 12);
buffer.put("FOO", 12, 0);
assert_eq!(buffer.cells.len(), 12);
buffer.put("FOO", 24, 0);
assert_eq!(buffer.cells.len(), 12);
buffer.put("FOO", 0, 1);
assert_eq!(buffer.cells.len(), 24);
}
}