From 631fdd8b737c449303332dd183db479dec51cf1b Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Sat, 27 Feb 2021 22:15:05 -0600 Subject: [PATCH] add drawbuf --- src/ui/drawbuf.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++ src/ui/mod.rs | 40 +++++++++++++++++++++-------------- src/ui/table.rs | 40 +++++++++++++++++------------------ src/ui/tabs.rs | 8 +++---- src/ui/widget.rs | 4 ++-- 5 files changed, 103 insertions(+), 42 deletions(-) create mode 100644 src/ui/drawbuf.rs diff --git a/src/ui/drawbuf.rs b/src/ui/drawbuf.rs new file mode 100644 index 0000000..5c7c54c --- /dev/null +++ b/src/ui/drawbuf.rs @@ -0,0 +1,53 @@ +use std::ops::Range; + +use anyhow::Result; +use crossterm::{ + cursor::MoveTo, + style::{Color, SetBackgroundColor, SetForegroundColor}, +}; + +use super::{Rect, Screen}; + +pub struct DrawBuf { + rect: Rect, + buffer: Vec, + dirty: Vec<(u16, Range)>, +} + +#[derive(Clone, Copy)] +struct Cell { + sym: char, + fg: Color, + bg: Color, +} + +impl DrawBuf { + pub fn new(rect: Rect) -> Self { + DrawBuf { + rect, + buffer: vec![ + Cell { + sym: ' ', + fg: Color::Reset, + bg: Color::Reset + }; + (rect.w * rect.h) as usize + ], + dirty: (0..rect.h).map(|row| (row, 0..rect.w)).collect(), + } + } + + pub fn draw(&mut self, w: &mut Screen) -> Result<()> { + for (row, range) in self.dirty.drain(..) { + queue!(w, MoveTo(row, range.start))?; + for i in range { + let idx = row * self.rect.w + i; + let cell = &self.buffer[idx as usize]; + queue!(w, SetForegroundColor(cell.fg), SetBackgroundColor(cell.bg),)?; + println!("{}", cell.sym); + } + } + + Ok(()) + } +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 6254102..0236839 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,5 +1,6 @@ //! UI module +mod drawbuf; mod table; mod tabs; mod widget; @@ -11,15 +12,16 @@ use std::time::Duration; use anyhow::Result; use chrono::Local; use crossterm::{ - cursor, + cursor::{self, MoveTo}, event::{self, Event, KeyCode, KeyEvent}, - style::{self, Color}, - terminal::{self, ClearType}, + style::{self, Color, SetBackgroundColor, SetForegroundColor}, + terminal::{self, Clear, ClearType}, }; use tokio::time; use crate::ExitSender; +use self::drawbuf::DrawBuf; use self::table::Table; use self::tabs::Tabs; use self::widget::Widget; @@ -29,13 +31,14 @@ const FRAME_DURATION: Duration = Duration::from_millis(20); /// Type alias for the screen object we're drawing to pub type Screen = Stdout; -/// X Y W H -#[derive(Copy, Clone)] +/// Rectangle +#[derive(Copy, Clone, Debug)] +#[allow(missing_docs)] pub struct Rect { - x: u16, - y: u16, - w: u16, - h: u16 + pub x: u16, + pub y: u16, + pub w: u16, + pub h: u16, } impl Rect { @@ -50,6 +53,10 @@ pub async fn run_ui(mut w: Stdout, exit: ExitSender) -> Result<()> { execute!(w, cursor::Hide, terminal::EnterAlternateScreen)?; terminal::enable_raw_mode()?; + let (term_width, term_height) = terminal::size()?; + let bounds = Rect::new(0, 0, term_width, term_height); + let mut drawbuf = DrawBuf::new(bounds); + let mut table = Table::default(); table.push_row(vec!["ur mom Lol!".to_owned()]); table.push_row(vec!["hek".to_owned()]); @@ -60,19 +67,20 @@ pub async fn run_ui(mut w: Stdout, exit: ExitSender) -> Result<()> { loop { queue!( w, - style::SetBackgroundColor(Color::Reset), - style::SetForegroundColor(Color::Reset), - terminal::Clear(ClearType::All), - cursor::MoveTo(0, 0), + SetBackgroundColor(Color::Reset), + SetForegroundColor(Color::Reset), + Clear(ClearType::All), + MoveTo(0, 0), )?; - let now = Local::now(); - println!("time {}", now); + // let now = Local::now(); + // println!("time {}", now); let (term_width, term_height) = terminal::size()?; let bounds = Rect::new(5, 5, term_width - 10, term_height - 10); // table.draw(&mut w, bounds)?; - tabs.draw(&mut w, bounds)?; + // tabs.draw(&mut w, bounds)?; + drawbuf.draw(&mut w)?; w.flush()?; // approx 60fps diff --git a/src/ui/table.rs b/src/ui/table.rs index 14aa8a6..26c6019 100644 --- a/src/ui/table.rs +++ b/src/ui/table.rs @@ -7,7 +7,7 @@ use crossterm::{ style::{self, Color}, }; -use super::{Rect, Screen, Widget}; +use super::{DrawBuf, Rect, Screen, Widget}; #[derive(Default)] pub struct Table { @@ -45,7 +45,7 @@ impl Widget for Table { } } - fn draw(&self, w: &mut Screen, rect: Rect) -> Result<()> { + fn draw(&self, buf: &mut DrawBuf, rect: Rect) -> Result<()> { if !self.rows.is_empty() { let mut columns = Vec::new(); for row in self.rows.iter() { @@ -59,20 +59,20 @@ impl Widget for Table { } for (i, row) in self.rows.iter().enumerate() { - queue!(w, cursor::MoveTo(rect.x, rect.y + i as u16))?; + // queue!(w, cursor::MoveTo(rect.x, rect.y + i as u16))?; if let Some(v) = self.selected_row { if v == i as u16 { - queue!( - w, - style::SetBackgroundColor(Color::White), - style::SetForegroundColor(Color::Black) - )?; + // queue!( + // w, + // style::SetBackgroundColor(Color::White), + // style::SetForegroundColor(Color::Black) + // )?; } else { - queue!( - w, - style::SetForegroundColor(Color::White), - style::SetBackgroundColor(Color::Black) - )?; + // queue!( + // w, + // style::SetForegroundColor(Color::White), + // style::SetBackgroundColor(Color::Black) + // )?; } } let mut s = String::with_capacity(rect.w as usize); @@ -89,20 +89,20 @@ impl Widget for Table { } let d = "\u{b7}".repeat(rect.w as usize); - queue!( - w, - style::SetBackgroundColor(Color::Black), - style::SetForegroundColor(Color::White) - )?; + // queue!( + // w, + // style::SetBackgroundColor(Color::Black), + // style::SetForegroundColor(Color::White) + // )?; for j in self.rows.len() as u16..rect.h { - queue!(w, cursor::MoveTo(rect.x, rect.y + j))?; + // queue!(w, cursor::MoveTo(rect.x, rect.y + j))?; println!("{}", d); } } else { let msg = "Nothing in this table!"; let x = rect.x + (rect.w - msg.len() as u16) / 2; let y = rect.y + rect.h / 2; - queue!(w, cursor::MoveTo(x, y))?; + // queue!(w, cursor::MoveTo(x, y))?; println!("{}", msg); } Ok(()) diff --git a/src/ui/tabs.rs b/src/ui/tabs.rs index 92eae4a..bafb9a1 100644 --- a/src/ui/tabs.rs +++ b/src/ui/tabs.rs @@ -4,7 +4,7 @@ use std::io::Write; use anyhow::Result; use crossterm::{cursor::MoveTo, event::Event}; -use super::{Rect, Screen, Widget}; +use super::{DrawBuf, Rect, Screen, Widget}; pub struct Tabs { id_incr: usize, @@ -35,15 +35,15 @@ impl Tabs { impl Widget for Tabs { fn update(&mut self, event: Option) {} - fn draw(&self, w: &mut Screen, rect: Rect) -> Result<()> { - queue!(w, MoveTo(rect.x, rect.y))?; + fn draw(&self, buf: &mut DrawBuf, rect: Rect) -> Result<()> { + // queue!(w, MoveTo(rect.x, rect.y))?; for (id, name) in self.names.iter() { println!(" {} ", name); } let new_rect = Rect::new(rect.x, rect.y + 1, rect.w, rect.h - 1); if let Some(widget) = self.contents.get(&self.active_id) { - widget.draw(w, new_rect)?; + widget.draw(buf, new_rect)?; } Ok(()) diff --git a/src/ui/widget.rs b/src/ui/widget.rs index 7e53c28..2669db0 100644 --- a/src/ui/widget.rs +++ b/src/ui/widget.rs @@ -3,14 +3,14 @@ use std::io::Write; use anyhow::Result; use crossterm::event::Event; -use super::{Rect, Screen}; +use super::{DrawBuf, Rect, Screen}; pub trait Widget { /// Updates the widget given an event fn update(&mut self, event: Option); /// Draws this UI element to the screen - fn draw(&self, w: &mut Screen, rect: Rect) -> Result<()>; + fn draw(&self, buf: &mut DrawBuf, rect: Rect) -> Result<()>; /// Invalidates this UI element, queueing it for redraw fn invalidate(&mut self);