Make width measuring optional and disabled by default

This commit is contained in:
Joscha 2022-06-08 17:38:38 +02:00
parent 33264b4aec
commit 333cf74fba
5 changed files with 47 additions and 27 deletions

View file

@ -1,7 +1,7 @@
use crossterm::event::Event; use crossterm::event::Event;
use crossterm::style::{ContentStyle, Stylize}; use crossterm::style::{ContentStyle, Stylize};
use toss::frame::{Frame, Pos}; use toss::frame::{Frame, Pos};
use toss::terminal::{Redraw, Terminal}; use toss::terminal::Terminal;
fn draw(f: &mut Frame) { fn draw(f: &mut Frame) {
f.write( f.write(
@ -24,8 +24,11 @@ fn render_frame(term: &mut Terminal) {
term.autoresize().unwrap(); term.autoresize().unwrap();
draw(term.frame()); draw(term.frame());
term.present().unwrap();
if term.present().unwrap() == Redraw::NotRequired { if term.measuring_required() {
term.measure_widths().unwrap();
} else {
break; break;
} }
} }
@ -34,6 +37,7 @@ fn render_frame(term: &mut Terminal) {
fn main() { fn main() {
// Automatically enters alternate screen and enables raw mode // Automatically enters alternate screen and enables raw mode
let mut term = Terminal::new().unwrap(); let mut term = Terminal::new().unwrap();
term.set_measuring(true);
loop { loop {
// Render and display a frame. A full frame is displayed on the terminal // Render and display a frame. A full frame is displayed on the terminal

View file

@ -1,7 +1,7 @@
use crossterm::event::Event; use crossterm::event::Event;
use crossterm::style::{ContentStyle, Stylize}; use crossterm::style::{ContentStyle, Stylize};
use toss::frame::{Frame, Pos}; use toss::frame::{Frame, Pos};
use toss::terminal::{Redraw, Terminal}; use toss::terminal::Terminal;
fn draw(f: &mut Frame) { fn draw(f: &mut Frame) {
f.write( f.write(
@ -65,7 +65,9 @@ fn render_frame(term: &mut Terminal) {
draw(term.frame()); draw(term.frame());
if term.present().unwrap() == Redraw::NotRequired { if term.measuring_required() {
term.measure_widths().unwrap();
} else {
break; break;
} }
} }
@ -74,6 +76,7 @@ fn render_frame(term: &mut Terminal) {
fn main() { fn main() {
// Automatically enters alternate screen and enables raw mode // Automatically enters alternate screen and enables raw mode
let mut term = Terminal::new().unwrap(); let mut term = Terminal::new().unwrap();
term.set_measuring(true);
loop { loop {
// Render and display a frame. A full frame is displayed on the terminal // Render and display a frame. A full frame is displayed on the terminal

View file

@ -1,7 +1,7 @@
use crossterm::event::Event; use crossterm::event::Event;
use crossterm::style::ContentStyle; use crossterm::style::ContentStyle;
use toss::frame::{Frame, Pos}; use toss::frame::{Frame, Pos};
use toss::terminal::{Redraw, Terminal}; use toss::terminal::Terminal;
fn draw(f: &mut Frame) { fn draw(f: &mut Frame) {
let text = concat!( let text = concat!(
@ -36,7 +36,9 @@ fn render_frame(term: &mut Terminal) {
draw(term.frame()); draw(term.frame());
if term.present().unwrap() == Redraw::NotRequired { if term.measuring_required() {
term.measure_widths().unwrap();
} else {
break; break;
} }
} }
@ -45,6 +47,7 @@ fn render_frame(term: &mut Terminal) {
fn main() { fn main() {
// Automatically enters alternate screen and enables raw mode // Automatically enters alternate screen and enables raw mode
let mut term = Terminal::new().unwrap(); let mut term = Terminal::new().unwrap();
term.set_measuring(true);
loop { loop {
// Render and display a frame. A full frame is displayed on the terminal // Render and display a frame. A full frame is displayed on the terminal

View file

@ -11,12 +11,6 @@ use crossterm::{ExecutableCommand, QueueableCommand};
use crate::buffer::{Buffer, Size}; use crate::buffer::{Buffer, Size};
use crate::frame::Frame; use crate::frame::Frame;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Redraw {
Required,
NotRequired,
}
pub struct Terminal { pub struct Terminal {
/// Render target. /// Render target.
out: Box<dyn Write>, out: Box<dyn Write>,
@ -54,6 +48,24 @@ impl Terminal {
Ok(result) 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 /// Resize the frame and other internal buffers if the terminal size has
/// changed. /// changed.
pub fn autoresize(&mut self) -> io::Result<()> { pub fn autoresize(&mut self) -> io::Result<()> {
@ -77,19 +89,7 @@ impl Terminal {
/// ///
/// After calling this function, the frame returned by [`Self::frame`] will /// After calling this function, the frame returned by [`Self::frame`] will
/// be empty again and have no cursor position. /// be empty again and have no cursor position.
pub fn present(&mut self) -> io::Result<Redraw> { 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);
}
if self.full_redraw { if self.full_redraw {
io::stdout().queue(Clear(ClearType::All))?; io::stdout().queue(Clear(ClearType::All))?;
self.prev_frame_buffer.reset(); // Because the screen is now empty 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); mem::swap(&mut self.prev_frame_buffer, &mut self.frame.buffer);
self.frame.reset(); self.frame.reset();
Ok(Redraw::NotRequired) Ok(())
} }
fn draw_differences(&mut self) -> io::Result<()> { fn draw_differences(&mut self) -> io::Result<()> {

View file

@ -11,6 +11,7 @@ use unicode_width::UnicodeWidthStr;
/// Measures and stores the with (in terminal coordinates) of graphemes. /// Measures and stores the with (in terminal coordinates) of graphemes.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct WidthDB { pub struct WidthDB {
pub active: bool,
known: HashMap<String, u8>, known: HashMap<String, u8>,
requested: HashSet<String>, requested: HashSet<String>,
} }
@ -22,6 +23,9 @@ impl WidthDB {
/// Unicode Standard Annex #11. /// Unicode Standard Annex #11.
pub fn grapheme_width(&mut self, grapheme: &str) -> u8 { pub fn grapheme_width(&mut self, grapheme: &str) -> u8 {
assert_eq!(Some(grapheme), grapheme.graphemes(true).next()); assert_eq!(Some(grapheme), grapheme.graphemes(true).next());
if !self.active {
return grapheme.width() as u8;
}
if let Some(width) = self.known.get(grapheme) { if let Some(width) = self.known.get(grapheme) {
*width *width
} else { } else {
@ -35,6 +39,9 @@ impl WidthDB {
/// If the width of a grapheme has not been measured yet, it is estimated /// If the width of a grapheme has not been measured yet, it is estimated
/// using the Unicode Standard Annex #11. /// using the Unicode Standard Annex #11.
pub fn width(&mut self, s: &str) -> usize { pub fn width(&mut self, s: &str) -> usize {
if !self.active {
return s.width();
}
let mut total: usize = 0; let mut total: usize = 0;
for grapheme in s.graphemes(true) { for grapheme in s.graphemes(true) {
total += if let Some(width) = self.known.get(grapheme) { 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 /// Whether any new graphemes have been seen since the last time
/// [`Self::measure_widths`] was called. /// [`Self::measure_widths`] was called.
pub fn measuring_required(&self) -> bool { 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 /// 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 /// the terminal. After it finishes, the terminal's contents should be
/// assumed to be garbage and a full redraw should be performed. /// assumed to be garbage and a full redraw should be performed.
pub fn measure_widths(&mut self, out: &mut impl Write) -> io::Result<()> { pub fn measure_widths(&mut self, out: &mut impl Write) -> io::Result<()> {
if !self.active {
return Ok(());
}
for grapheme in self.requested.drain() { for grapheme in self.requested.drain() {
out.queue(Clear(ClearType::All))? out.queue(Clear(ClearType::All))?
.queue(MoveTo(0, 0))? .queue(MoveTo(0, 0))?