diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 57cc976..fbad68c 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -1,7 +1,7 @@ use crossterm::event::Event; use crossterm::style::{ContentStyle, Stylize}; use toss::frame::{Frame, Pos}; -use toss::terminal::{Redraw, Terminal}; +use toss::terminal::Terminal; fn draw(f: &mut Frame) { f.write( @@ -24,8 +24,11 @@ fn render_frame(term: &mut Terminal) { term.autoresize().unwrap(); draw(term.frame()); + term.present().unwrap(); - if term.present().unwrap() == Redraw::NotRequired { + if term.measuring_required() { + term.measure_widths().unwrap(); + } else { break; } } @@ -34,6 +37,7 @@ fn render_frame(term: &mut Terminal) { fn main() { // Automatically enters alternate screen and enables raw mode let mut term = Terminal::new().unwrap(); + term.set_measuring(true); loop { // Render and display a frame. A full frame is displayed on the terminal diff --git a/examples/overlapping_graphemes.rs b/examples/overlapping_graphemes.rs index db98bae..061f830 100644 --- a/examples/overlapping_graphemes.rs +++ b/examples/overlapping_graphemes.rs @@ -1,7 +1,7 @@ use crossterm::event::Event; use crossterm::style::{ContentStyle, Stylize}; use toss::frame::{Frame, Pos}; -use toss::terminal::{Redraw, Terminal}; +use toss::terminal::Terminal; fn draw(f: &mut Frame) { f.write( @@ -65,7 +65,9 @@ fn render_frame(term: &mut Terminal) { draw(term.frame()); - if term.present().unwrap() == Redraw::NotRequired { + if term.measuring_required() { + term.measure_widths().unwrap(); + } else { break; } } @@ -74,6 +76,7 @@ fn render_frame(term: &mut Terminal) { fn main() { // Automatically enters alternate screen and enables raw mode let mut term = Terminal::new().unwrap(); + term.set_measuring(true); loop { // Render and display a frame. A full frame is displayed on the terminal diff --git a/examples/text_wrapping.rs b/examples/text_wrapping.rs index f150142..19d4172 100644 --- a/examples/text_wrapping.rs +++ b/examples/text_wrapping.rs @@ -1,7 +1,7 @@ use crossterm::event::Event; use crossterm::style::ContentStyle; use toss::frame::{Frame, Pos}; -use toss::terminal::{Redraw, Terminal}; +use toss::terminal::Terminal; fn draw(f: &mut Frame) { let text = concat!( @@ -36,7 +36,9 @@ fn render_frame(term: &mut Terminal) { draw(term.frame()); - if term.present().unwrap() == Redraw::NotRequired { + if term.measuring_required() { + term.measure_widths().unwrap(); + } else { break; } } @@ -45,6 +47,7 @@ fn render_frame(term: &mut Terminal) { fn main() { // Automatically enters alternate screen and enables raw mode let mut term = Terminal::new().unwrap(); + term.set_measuring(true); loop { // Render and display a frame. A full frame is displayed on the terminal diff --git a/src/terminal.rs b/src/terminal.rs index eb5645e..91706f9 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -11,12 +11,6 @@ use crossterm::{ExecutableCommand, QueueableCommand}; use crate::buffer::{Buffer, Size}; use crate::frame::Frame; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Redraw { - Required, - NotRequired, -} - pub struct Terminal { /// Render target. out: Box, @@ -54,6 +48,24 @@ impl Terminal { Ok(result) } + pub fn set_measuring(&mut self, active: bool) { + self.frame.widthdb.active = active; + } + + pub fn measuring(&self) -> bool { + self.frame.widthdb.active + } + + pub fn measuring_required(&self) -> bool { + self.frame.widthdb.measuring_required() + } + + pub fn measure_widths(&mut self) -> io::Result<()> { + self.frame.widthdb.measure_widths(&mut self.out)?; + self.full_redraw = true; + Ok(()) + } + /// Resize the frame and other internal buffers if the terminal size has /// changed. pub fn autoresize(&mut self) -> io::Result<()> { @@ -77,19 +89,7 @@ impl Terminal { /// /// After calling this function, the frame returned by [`Self::frame`] will /// be empty again and have no cursor position. - pub fn present(&mut self) -> io::Result { - if self.frame.widthdb.measuring_required() { - self.frame.widthdb.measure_widths(&mut self.out)?; - // Since we messed up the screen by measuring widths, we'll need to - // do a full redraw the next time around. - self.full_redraw = true; - // Throwing away the current frame because its content were rendered - // with unconfirmed width data. Also, this function guarantees that - // after it is called, the frame is empty. - self.frame.reset(); - return Ok(Redraw::Required); - } - + pub fn present(&mut self) -> io::Result<()> { if self.full_redraw { io::stdout().queue(Clear(ClearType::All))?; self.prev_frame_buffer.reset(); // Because the screen is now empty @@ -103,7 +103,7 @@ impl Terminal { mem::swap(&mut self.prev_frame_buffer, &mut self.frame.buffer); self.frame.reset(); - Ok(Redraw::NotRequired) + Ok(()) } fn draw_differences(&mut self) -> io::Result<()> { diff --git a/src/widthdb.rs b/src/widthdb.rs index 6af28c3..9072995 100644 --- a/src/widthdb.rs +++ b/src/widthdb.rs @@ -11,6 +11,7 @@ use unicode_width::UnicodeWidthStr; /// Measures and stores the with (in terminal coordinates) of graphemes. #[derive(Debug, Default)] pub struct WidthDB { + pub active: bool, known: HashMap, requested: HashSet, } @@ -22,6 +23,9 @@ impl WidthDB { /// Unicode Standard Annex #11. pub fn grapheme_width(&mut self, grapheme: &str) -> u8 { assert_eq!(Some(grapheme), grapheme.graphemes(true).next()); + if !self.active { + return grapheme.width() as u8; + } if let Some(width) = self.known.get(grapheme) { *width } else { @@ -35,6 +39,9 @@ impl WidthDB { /// If the width of a grapheme has not been measured yet, it is estimated /// using the Unicode Standard Annex #11. pub fn width(&mut self, s: &str) -> usize { + if !self.active { + return s.width(); + } let mut total: usize = 0; for grapheme in s.graphemes(true) { total += if let Some(width) = self.known.get(grapheme) { @@ -50,7 +57,7 @@ impl WidthDB { /// Whether any new graphemes have been seen since the last time /// [`Self::measure_widths`] was called. pub fn measuring_required(&self) -> bool { - !self.requested.is_empty() + self.active && !self.requested.is_empty() } /// Measure the width of all new graphemes that have been seen since the @@ -60,6 +67,9 @@ impl WidthDB { /// the terminal. After it finishes, the terminal's contents should be /// assumed to be garbage and a full redraw should be performed. pub fn measure_widths(&mut self, out: &mut impl Write) -> io::Result<()> { + if !self.active { + return Ok(()); + } for grapheme in self.requested.drain() { out.queue(Clear(ClearType::All))? .queue(MoveTo(0, 0))?