add drawbuf

This commit is contained in:
Michael Zhang 2021-02-27 22:15:05 -06:00
parent 30b481ee64
commit 631fdd8b73
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
5 changed files with 103 additions and 42 deletions

53
src/ui/drawbuf.rs Normal file
View file

@ -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<Cell>,
dirty: Vec<(u16, Range<u16>)>,
}
#[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(())
}
}

View file

@ -1,5 +1,6 @@
//! UI module //! UI module
mod drawbuf;
mod table; mod table;
mod tabs; mod tabs;
mod widget; mod widget;
@ -11,15 +12,16 @@ use std::time::Duration;
use anyhow::Result; use anyhow::Result;
use chrono::Local; use chrono::Local;
use crossterm::{ use crossterm::{
cursor, cursor::{self, MoveTo},
event::{self, Event, KeyCode, KeyEvent}, event::{self, Event, KeyCode, KeyEvent},
style::{self, Color}, style::{self, Color, SetBackgroundColor, SetForegroundColor},
terminal::{self, ClearType}, terminal::{self, Clear, ClearType},
}; };
use tokio::time; use tokio::time;
use crate::ExitSender; use crate::ExitSender;
use self::drawbuf::DrawBuf;
use self::table::Table; use self::table::Table;
use self::tabs::Tabs; use self::tabs::Tabs;
use self::widget::Widget; 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 /// Type alias for the screen object we're drawing to
pub type Screen = Stdout; pub type Screen = Stdout;
/// X Y W H /// Rectangle
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
#[allow(missing_docs)]
pub struct Rect { pub struct Rect {
x: u16, pub x: u16,
y: u16, pub y: u16,
w: u16, pub w: u16,
h: u16 pub h: u16,
} }
impl Rect { impl Rect {
@ -50,6 +53,10 @@ pub async fn run_ui(mut w: Stdout, exit: ExitSender) -> Result<()> {
execute!(w, cursor::Hide, terminal::EnterAlternateScreen)?; execute!(w, cursor::Hide, terminal::EnterAlternateScreen)?;
terminal::enable_raw_mode()?; 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(); let mut table = Table::default();
table.push_row(vec!["ur mom Lol!".to_owned()]); table.push_row(vec!["ur mom Lol!".to_owned()]);
table.push_row(vec!["hek".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 { loop {
queue!( queue!(
w, w,
style::SetBackgroundColor(Color::Reset), SetBackgroundColor(Color::Reset),
style::SetForegroundColor(Color::Reset), SetForegroundColor(Color::Reset),
terminal::Clear(ClearType::All), Clear(ClearType::All),
cursor::MoveTo(0, 0), MoveTo(0, 0),
)?; )?;
let now = Local::now(); // let now = Local::now();
println!("time {}", now); // println!("time {}", now);
let (term_width, term_height) = terminal::size()?; let (term_width, term_height) = terminal::size()?;
let bounds = Rect::new(5, 5, term_width - 10, term_height - 10); let bounds = Rect::new(5, 5, term_width - 10, term_height - 10);
// table.draw(&mut w, bounds)?; // table.draw(&mut w, bounds)?;
tabs.draw(&mut w, bounds)?; // tabs.draw(&mut w, bounds)?;
drawbuf.draw(&mut w)?;
w.flush()?; w.flush()?;
// approx 60fps // approx 60fps

View file

@ -7,7 +7,7 @@ use crossterm::{
style::{self, Color}, style::{self, Color},
}; };
use super::{Rect, Screen, Widget}; use super::{DrawBuf, Rect, Screen, Widget};
#[derive(Default)] #[derive(Default)]
pub struct Table { 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() { if !self.rows.is_empty() {
let mut columns = Vec::new(); let mut columns = Vec::new();
for row in self.rows.iter() { for row in self.rows.iter() {
@ -59,20 +59,20 @@ impl Widget for Table {
} }
for (i, row) in self.rows.iter().enumerate() { 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 let Some(v) = self.selected_row {
if v == i as u16 { if v == i as u16 {
queue!( // queue!(
w, // w,
style::SetBackgroundColor(Color::White), // style::SetBackgroundColor(Color::White),
style::SetForegroundColor(Color::Black) // style::SetForegroundColor(Color::Black)
)?; // )?;
} else { } else {
queue!( // queue!(
w, // w,
style::SetForegroundColor(Color::White), // style::SetForegroundColor(Color::White),
style::SetBackgroundColor(Color::Black) // style::SetBackgroundColor(Color::Black)
)?; // )?;
} }
} }
let mut s = String::with_capacity(rect.w as usize); 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); let d = "\u{b7}".repeat(rect.w as usize);
queue!( // queue!(
w, // w,
style::SetBackgroundColor(Color::Black), // style::SetBackgroundColor(Color::Black),
style::SetForegroundColor(Color::White) // style::SetForegroundColor(Color::White)
)?; // )?;
for j in self.rows.len() as u16..rect.h { 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); println!("{}", d);
} }
} else { } else {
let msg = "Nothing in this table!"; let msg = "Nothing in this table!";
let x = rect.x + (rect.w - msg.len() as u16) / 2; let x = rect.x + (rect.w - msg.len() as u16) / 2;
let y = rect.y + rect.h / 2; let y = rect.y + rect.h / 2;
queue!(w, cursor::MoveTo(x, y))?; // queue!(w, cursor::MoveTo(x, y))?;
println!("{}", msg); println!("{}", msg);
} }
Ok(()) Ok(())

View file

@ -4,7 +4,7 @@ use std::io::Write;
use anyhow::Result; use anyhow::Result;
use crossterm::{cursor::MoveTo, event::Event}; use crossterm::{cursor::MoveTo, event::Event};
use super::{Rect, Screen, Widget}; use super::{DrawBuf, Rect, Screen, Widget};
pub struct Tabs { pub struct Tabs {
id_incr: usize, id_incr: usize,
@ -35,15 +35,15 @@ impl Tabs {
impl Widget for Tabs { impl Widget for Tabs {
fn update(&mut self, event: Option<Event>) {} fn update(&mut self, event: Option<Event>) {}
fn draw(&self, w: &mut Screen, rect: Rect) -> Result<()> { fn draw(&self, buf: &mut DrawBuf, rect: Rect) -> Result<()> {
queue!(w, MoveTo(rect.x, rect.y))?; // queue!(w, MoveTo(rect.x, rect.y))?;
for (id, name) in self.names.iter() { for (id, name) in self.names.iter() {
println!(" {} ", name); println!(" {} ", name);
} }
let new_rect = Rect::new(rect.x, rect.y + 1, rect.w, rect.h - 1); 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) { if let Some(widget) = self.contents.get(&self.active_id) {
widget.draw(w, new_rect)?; widget.draw(buf, new_rect)?;
} }
Ok(()) Ok(())

View file

@ -3,14 +3,14 @@ use std::io::Write;
use anyhow::Result; use anyhow::Result;
use crossterm::event::Event; use crossterm::event::Event;
use super::{Rect, Screen}; use super::{DrawBuf, Rect, Screen};
pub trait Widget { pub trait Widget {
/// Updates the widget given an event /// Updates the widget given an event
fn update(&mut self, event: Option<Event>); fn update(&mut self, event: Option<Event>);
/// Draws this UI element to the screen /// 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 /// Invalidates this UI element, queueing it for redraw
fn invalidate(&mut self); fn invalidate(&mut self);