mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
195 lines
5.7 KiB
Rust
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);
|
|
|
|
}
|
|
}
|