ui: draw to terminal with horizontal sweeping

sweep each line in the dirty areas and avoid redraws because of area
overlap
embed
Manos Pitsidianakis 2019-06-10 15:56:53 +03:00
parent cb83f9fe05
commit e0df6ce22d
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
1 changed files with 73 additions and 47 deletions

View File

@ -368,69 +368,95 @@ impl State {
for i in 0..self.components.len() { for i in 0..self.components.len() {
self.draw_component(i); self.draw_component(i);
} }
let areas: Vec<Area> = self.context.dirty_areas.drain(0..).collect(); let mut areas: Vec<Area> = self.context.dirty_areas.drain(0..).collect();
/* Sort by x_start, ie upper_left corner's x coordinate */
areas.sort_by(|a, b| (a.0).0.partial_cmp(&(b.0).0).unwrap());
/* draw each dirty area */ /* draw each dirty area */
for a in areas { let cols = self.cols;
self.draw_area(a); let rows = self.rows;
} for y in 0..rows {
} let mut segment = None;
for ((x_start, y_start), (x_end, y_end)) in &areas {
/// Draw only a specific `area` on the screen. if y < *y_start || y > *y_end {
fn draw_area(&mut self, area: Area) { continue;
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
for y in get_y(upper_left)..=get_y(bottom_right) {
write!(
self.stdout(),
"{}",
cursor::Goto(get_x(upper_left) as u16 + 1, (y + 1) as u16)
)
.unwrap();
for x in get_x(upper_left)..=get_x(bottom_right) {
let c = self.grid[(x, y)];
if c.bg() != Color::Default {
write!(self.stdout(), "{}", termion::color::Bg(c.bg().as_termion())).unwrap();
} }
if c.fg() != Color::Default { if let Some((x_start, x_end)) = segment.take() {
write!(self.stdout(), "{}", termion::color::Fg(c.fg().as_termion())).unwrap(); self.draw_horizontal_segment(x_start, x_end, y);
} }
write!(self.stdout(), "{}", c.ch()).unwrap(); match segment {
if c.bg() != Color::Default { ref mut s @ None => {
write!( *s = Some((*x_start, *x_end));
self.stdout(), }
"{}", ref mut s @ Some(_) if s.unwrap().1 < *x_start => {
termion::color::Bg(termion::color::Reset) self.draw_horizontal_segment(s.unwrap().0, s.unwrap().1, y);
) *s = Some((*x_start, *x_end));
.unwrap(); }
} ref mut s @ Some(_) if s.unwrap().1 < *x_end => {
if c.fg() != Color::Default { self.draw_horizontal_segment(s.unwrap().0, s.unwrap().1, y);
write!( *s = Some((s.unwrap().1, *x_end));
self.stdout(), }
"{}", Some((_, ref mut x)) => {
termion::color::Fg(termion::color::Reset) *x = *x_end;
) }
.unwrap();
} }
} }
if let Some((x_start, x_end)) = segment {
self.draw_horizontal_segment(x_start, x_end, y);
}
} }
self.flush(); self.flush();
} }
/// Draw only a specific `area` on the screen.
fn draw_horizontal_segment(&mut self, x_start: usize, x_end: usize, y: usize) {
write!(
self.stdout(),
"{}",
cursor::Goto(x_start as u16 + 1, (y + 1) as u16)
)
.unwrap();
for x in x_start..=x_end {
let c = self.grid[(x, y)];
if c.bg() != Color::Default {
write!(self.stdout(), "{}", termion::color::Bg(c.bg().as_termion())).unwrap();
}
if c.fg() != Color::Default {
write!(self.stdout(), "{}", termion::color::Fg(c.fg().as_termion())).unwrap();
}
write!(self.stdout(), "{}", c.ch()).unwrap();
let mut b = [0; 4];
if c.bg() != Color::Default {
write!(
self.stdout(),
"{}",
termion::color::Bg(termion::color::Reset)
)
.unwrap();
}
if c.fg() != Color::Default {
write!(
self.stdout(),
"{}",
termion::color::Fg(termion::color::Reset)
)
.unwrap();
}
}
}
/// Draw the entire screen from scratch. /// Draw the entire screen from scratch.
pub fn render(&mut self) { pub fn render(&mut self) {
self.update_size(); self.update_size();
/* draw each component */
for i in 0..self.components.len() {
self.draw_component(i);
}
let cols = self.cols; let cols = self.cols;
let rows = self.rows; let rows = self.rows;
self.context
.dirty_areas
.push_back(((0, 0), (cols - 1, rows - 1)));
self.draw_area(((0, 0), (cols - 1, rows - 1))); self.redraw();
} }
pub fn draw_component(&mut self, idx: usize) { pub fn draw_component(&mut self, idx: usize) {
let component = &mut self.components[idx]; let component = &mut self.components[idx];
let upper_left = (0, 0); let upper_left = (0, 0);